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,
230 : circuitOp(circuitOp), instanceGraph(instanceGraph),
231 enableAnnotationWarning(enableAnnotationWarning),
232 lowerToCore(lowerToCore), verificationFlavor(verificationFlavor),
233 nlaTable(nlaTable), macroTable(macroTable) {
234 auto *
context = circuitOp.getContext();
238 AnnotationSet(circuitOp).getAnnotation(testBenchDirAnnoClass)) {
239 auto dirName = tbAnno.getMember<StringAttr>(
"dirname");
240 testBenchDirectory = hw::OutputFileAttr::getAsDirectory(
241 context, dirName.getValue(),
false,
true);
245 if (
auto module = dyn_cast<FModuleLike>(op)) {
257 testHarness =
nullptr;
258 }
else if (dut == testHarness) {
259 testHarness =
nullptr;
264 auto inDUT = [&](igraph::ModuleOpInterface child) {
266 if (
auto inst = instRec->getInstance<InstanceOp>())
267 return inst.getLowerToBind() || inst.getDoNotPrint();
270 if (
auto parent = dyn_cast<igraph::ModuleOpInterface>(*dut))
271 return getInstanceGraph().isAncestor(child, parent, isPhony);
274 circuitOp->walk([&](FModuleLike moduleOp) {
276 dutModules.insert(moduleOp);
280 Operation *getNewModule(Operation *oldModule) {
281 auto it = oldToNewModuleMap.find(oldModule);
282 return it != oldToNewModuleMap.end() ? it->second :
nullptr;
285 Operation *getOldModule(Operation *newModule) {
286 auto it = newToOldModuleMap.find(newModule);
287 return it != newToOldModuleMap.end() ? it->second :
nullptr;
290 void recordModuleMapping(Operation *oldFMod, Operation *newHWMod) {
291 oldToNewModuleMap[oldFMod] = newHWMod;
292 newToOldModuleMap[newHWMod] = oldFMod;
297 void processRemainingAnnotations(Operation *op,
const AnnotationSet &annoSet);
303 void addBind(sv::BindOp op) {
304 std::lock_guard<std::mutex> lock(bindsMutex);
310 hw::TypeAliasType getTypeAlias(Type rawType, BaseTypeAliasType firAliasType,
313 auto hwAlias = typeAliases.getTypedecl(firAliasType);
316 assert(!typeAliases.isFrozen() &&
317 "type aliases cannot be generated after its frozen");
318 return typeAliases.addTypedecl(rawType, firAliasType, typeLoc);
321 FModuleLike getDut() {
return dut; }
322 FModuleLike getTestHarness() {
return testHarness; }
328 bool isInDUT(igraph::ModuleOpInterface child) {
329 if (
auto hwModule = dyn_cast<hw::HWModuleOp>(child.getOperation()))
330 child = cast<igraph::ModuleOpInterface>(getOldModule(hwModule));
331 return dutModules.contains(child);
334 hw::OutputFileAttr getTestBenchDirectory() {
return testBenchDirectory; }
339 bool isInTestHarness(igraph::ModuleOpInterface mod) {
return !isInDUT(mod); }
346 Type
lowerType(Type type, Location loc) {
347 return ::lowerType(type, loc,
348 [&](Type rawType, BaseTypeAliasType firrtlType,
349 Location typeLoc) -> hw::TypeAliasType {
350 return getTypeAlias(rawType, firrtlType, typeLoc);
356 llvm::sys::SmartScopedLock<true> lock(verbatimSourcesMutex);
357 auto it = verbatimSourcesByFileName.find(fileName);
358 return it != verbatimSourcesByFileName.end() ? it->second :
nullptr;
363 void registerVerbatimSource(StringRef fileName,
365 llvm::sys::SmartScopedLock<true> lock(verbatimSourcesMutex);
366 verbatimSourcesByFileName[fileName] = verbatimOp;
370 emit::FileOp getEmitFileForFile(StringRef fileName) {
371 llvm::sys::SmartScopedLock<true> lock(emitFilesMutex);
372 auto it = emitFilesByFileName.find(fileName);
373 return it != emitFilesByFileName.end() ? it->second :
nullptr;
378 void registerEmitFile(StringRef fileName, emit::FileOp fileOp) {
379 llvm::sys::SmartScopedLock<true> lock(emitFilesMutex);
380 emitFilesByFileName[fileName] = fileOp;
384 friend struct FIRRTLModuleLowering;
385 friend struct FIRRTLLowering;
386 CircuitLoweringState(
const CircuitLoweringState &) =
delete;
387 void operator=(
const CircuitLoweringState &) =
delete;
390 DenseMap<Operation *, Operation *> oldToNewModuleMap;
393 DenseMap<Operation *, Operation *> newToOldModuleMap;
404 DenseSet<igraph::ModuleOpInterface> dutModules;
408 StringSet<> pendingAnnotations;
409 const bool enableAnnotationWarning;
410 std::mutex annotationPrintingMtx;
412 const bool lowerToCore;
417 SmallVector<sv::BindOp> binds;
420 std::mutex bindsMutex;
428 FModuleLike testHarness;
431 hw::OutputFileAttr testBenchDirectory;
435 DenseMap<std::pair<Attribute, Attribute>, Attribute> instanceForceNames;
438 SetVector<StringAttr> macroDeclNames;
439 std::mutex macroDeclMutex;
441 void addMacroDecl(StringAttr name) {
442 std::unique_lock<std::mutex> lock(macroDeclMutex);
443 macroDeclNames.insert(name);
448 DenseMap<hw::HWModuleOp, SetVector<Attribute>> fragments;
449 llvm::sys::SmartMutex<true> fragmentsMutex;
452 llvm::sys::SmartScopedLock<true> lock(fragmentsMutex);
453 fragments[module].insert(
454 FlatSymbolRefAttr::get(circuitOp.getContext(), fragment));
469 struct RecordTypeAlias {
471 RecordTypeAlias(CircuitOp c) : circuitOp(c) {}
473 hw::TypeAliasType getTypedecl(BaseTypeAliasType firAlias)
const {
474 auto iter = firrtlTypeToAliasTypeMap.find(firAlias);
475 if (iter != firrtlTypeToAliasTypeMap.end())
480 bool isFrozen() {
return frozen; }
482 void freeze() { frozen =
true; }
484 hw::TypeAliasType addTypedecl(Type rawType, BaseTypeAliasType firAlias,
486 assert(!frozen &&
"Record already frozen, cannot be updated");
489 auto b = ImplicitLocOpBuilder::atBlockBegin(
491 &circuitOp->getParentRegion()->getBlocks().back());
493 b,
b.getStringAttr(circuitOp.getName() +
"__TYPESCOPE_"));
494 typeScope.getBodyRegion().push_back(
new Block());
496 auto typeName = firAlias.getName();
501 StringAttr::get(typeName.getContext(),
502 typeDeclNamespace.newName(typeName.getValue()));
504 auto typeScopeBuilder =
505 ImplicitLocOpBuilder::atBlockEnd(typeLoc, typeScope.getBodyBlock());
507 typeName, rawType,
nullptr);
508 auto hwAlias = hw::TypeAliasType::get(
509 SymbolRefAttr::get(typeScope.getSymNameAttr(),
510 {FlatSymbolRefAttr::get(typeDecl)}),
512 auto insert = firrtlTypeToAliasTypeMap.try_emplace(firAlias, hwAlias);
513 assert(insert.second &&
"Entry already exists, insert failed");
514 return insert.first->second;
523 DenseMap<Type, hw::TypeAliasType> firrtlTypeToAliasTypeMap;
531 RecordTypeAlias typeAliases = RecordTypeAlias(circuitOp);
534 llvm::StringMap<sv::SVVerbatimSourceOp> verbatimSourcesByFileName;
535 llvm::sys::SmartMutex<true> verbatimSourcesMutex;
538 llvm::StringMap<emit::FileOp> emitFilesByFileName;
539 llvm::sys::SmartMutex<true> emitFilesMutex;
545void CircuitLoweringState::processRemainingAnnotations(
547 if (!enableAnnotationWarning || annoSet.
empty())
549 std::lock_guard<std::mutex> lock(annotationPrintingMtx);
551 for (
auto a : annoSet) {
552 auto inserted = pendingAnnotations.insert(
a.getClass());
553 if (!inserted.second)
574 markDUTAnnoClass, metadataDirAnnoClass, testBenchDirAnnoClass,
579 extractGrandCentralAnnoClass,
582 extractAssertionsAnnoClass, extractAssumptionsAnnoClass,
583 extractCoverageAnnoClass,
587 moduleHierarchyAnnoClass, testHarnessHierarchyAnnoClass,
588 blackBoxTargetDirAnnoClass))
591 mlir::emitWarning(op->getLoc(),
"unprocessed annotation:'" +
a.getClass() +
592 "' still remaining after LowerToHW");
598struct FIRRTLModuleLowering
599 :
public circt::impl::LowerFIRRTLToHWBase<FIRRTLModuleLowering> {
601 void runOnOperation()
override;
602 void setEnableAnnotationWarning() { enableAnnotationWarning =
true; }
603 void setLowerToCore() { lowerToCore =
true; }
605 using LowerFIRRTLToHWBase<FIRRTLModuleLowering>::verificationFlavor;
608 void lowerFileHeader(CircuitOp op, CircuitLoweringState &
loweringState);
610 LogicalResult lowerPorts(ArrayRef<PortInfo> firrtlPorts,
611 SmallVectorImpl<hw::PortInfo> &ports,
612 Operation *moduleOp, StringRef moduleName,
614 bool handleForceNameAnnos(FModuleLike oldModule,
AnnotationSet &annos,
616 hw::HWModuleOp lowerModule(FModuleOp oldModule, Block *topLevelModule,
619 getVerbatimSourceForExtModule(FExtModuleOp oldModule, Block *topLevelModule,
621 hw::HWModuleLike lowerExtModule(FExtModuleOp oldModule, Block *topLevelModule,
624 lowerVerbatimExtModule(FExtModuleOp oldModule, Block *topLevelModule,
627 Block *topLevelModule,
631 lowerModulePortsAndMoveBody(FModuleOp oldModule,
hw::HWModuleOp newModule,
635 LogicalResult lowerFormalBody(verif::FormalOp formalOp,
637 LogicalResult lowerSimulationBody(verif::SimulationOp simulationOp,
639 LogicalResult lowerFileBody(emit::FileOp op);
646std::unique_ptr<mlir::Pass>
650 auto pass = std::make_unique<FIRRTLModuleLowering>();
651 if (enableAnnotationWarning)
652 pass->setEnableAnnotationWarning();
654 pass->setLowerToCore();
655 pass->verificationFlavor = verificationFlavor;
661void FIRRTLModuleLowering::runOnOperation() {
665 auto *topLevelModule = getOperation().getBody();
669 for (
auto &op : *topLevelModule) {
670 if ((circuit = dyn_cast<CircuitOp>(&op)))
677 auto *circuitBody = circuit.getBodyBlock();
681 CircuitLoweringState state(circuit, enableAnnotationWarning, lowerToCore,
682 verificationFlavor, getAnalysis<InstanceGraph>(),
683 &getAnalysis<NLATable>(),
684 getAnalysis<InstanceChoiceMacroTable>());
686 SmallVector<Operation *, 32> opsToProcess;
689 state.processRemainingAnnotations(circuit, circuitAnno);
692 for (
auto &op : make_early_inc_range(circuitBody->getOperations())) {
694 TypeSwitch<Operation *, LogicalResult>(&op)
695 .Case<FModuleOp>([&](
auto module) {
696 auto loweredMod = lowerModule(module, topLevelModule, state);
700 state.recordModuleMapping(&op, loweredMod);
701 opsToProcess.push_back(loweredMod);
703 module.walk([&](Operation *op) {
704 for (auto res : op->getResults()) {
706 type_dyn_cast<BaseTypeAliasType>(res.getType()))
707 state.lowerType(aliasType, op->getLoc());
710 return lowerModulePortsAndMoveBody(module, loweredMod, state);
712 .Case<FExtModuleOp>([&](
auto extModule) {
714 lowerExtModule(extModule, topLevelModule, state);
717 state.recordModuleMapping(&op, loweredMod);
720 .Case<FMemModuleOp>([&](
auto memModule) {
722 lowerMemModule(memModule, topLevelModule, state);
725 state.recordModuleMapping(&op, loweredMod);
728 .Case<FormalOp>([&](
auto oldOp) {
729 auto builder = OpBuilder::atBlockEnd(topLevelModule);
730 auto newOp = verif::FormalOp::create(builder, oldOp.getLoc(),
732 oldOp.getParametersAttr());
733 newOp.getBody().emplaceBlock();
734 state.recordModuleMapping(oldOp, newOp);
735 opsToProcess.push_back(newOp);
738 .Case<SimulationOp>([&](
auto oldOp) {
739 auto loc = oldOp.getLoc();
740 auto builder = OpBuilder::atBlockEnd(topLevelModule);
741 auto newOp = verif::SimulationOp::create(
742 builder, loc, oldOp.getNameAttr(), oldOp.getParametersAttr());
743 auto &body = newOp.getRegion().emplaceBlock();
744 body.addArgument(seq::ClockType::get(builder.getContext()), loc);
745 body.addArgument(builder.getI1Type(), loc);
746 state.recordModuleMapping(oldOp, newOp);
747 opsToProcess.push_back(newOp);
750 .Case<emit::FileOp>([&](
auto fileOp) {
751 fileOp->moveBefore(topLevelModule, topLevelModule->end());
752 opsToProcess.push_back(fileOp);
755 .Case<OptionOp, OptionCaseOp>([&](
auto) {
759 .Default([&](Operation *op) {
764 op->moveBefore(topLevelModule, topLevelModule->end());
770 return signalPassFailure();
773 state.typeAliases.freeze();
778 SmallVector<Attribute> dutHierarchyFiles;
779 SmallVector<Attribute> testHarnessHierarchyFiles;
780 circuitAnno.removeAnnotations([&](
Annotation annotation) {
781 if (annotation.
isClass(moduleHierarchyAnnoClass)) {
782 auto file = hw::OutputFileAttr::getFromFilename(
784 annotation.
getMember<StringAttr>(
"filename").getValue(),
786 dutHierarchyFiles.push_back(file);
789 if (annotation.
isClass(testHarnessHierarchyAnnoClass)) {
790 auto file = hw::OutputFileAttr::getFromFilename(
792 annotation.
getMember<StringAttr>(
"filename").getValue(),
796 if (state.getTestHarness())
797 testHarnessHierarchyFiles.push_back(file);
799 dutHierarchyFiles.push_back(file);
805 if (!dutHierarchyFiles.empty())
806 state.getNewModule(state.getDut())
808 ArrayAttr::get(&getContext(), dutHierarchyFiles));
809 if (!testHarnessHierarchyFiles.empty())
810 state.getNewModule(state.getTestHarness())
812 ArrayAttr::get(&getContext(), testHarnessHierarchyFiles));
816 mlir::failableParallelForEach(&getContext(), opsToProcess, [&](
auto op) {
820 return signalPassFailure();
823 for (
auto bind : state.binds) {
828 for (
auto &[module, fragments] : state.fragments)
830 ArrayAttr::
get(&getContext(), fragments.getArrayRef()));
833 for (
auto oldNew : state.oldToNewModuleMap)
834 oldNew.first->erase();
836 if (!state.macroDeclNames.empty()) {
837 ImplicitLocOpBuilder
b(UnknownLoc::get(&getContext()), circuit);
838 for (
auto name : state.macroDeclNames) {
839 sv::MacroDeclOp::create(b, name);
844 lowerFileHeader(circuit, state);
851void FIRRTLModuleLowering::lowerFileHeader(CircuitOp op,
852 CircuitLoweringState &state) {
855 ImplicitLocOpBuilder
b(UnknownLoc::get(&getContext()), op);
859 auto emitGuardedDefine = [&](StringRef guard, StringRef defName,
860 StringRef defineTrue =
"",
861 StringRef defineFalse = StringRef()) {
862 if (!defineFalse.data()) {
863 assert(defineTrue.data() &&
"didn't define anything");
865 b, guard, [&]() { sv::MacroDefOp::create(b, defName, defineTrue); });
870 if (defineTrue.data())
871 sv::MacroDefOp::create(b, defName, defineTrue);
873 [&]() { sv::MacroDefOp::create(b, defName, defineFalse); });
878 auto emitGuard = [&](
const char *guard, llvm::function_ref<void(
void)> body) {
880 b, guard, [] {}, body);
883 if (state.usedFileDescriptorLib) {
885 SmallVector<hw::ModulePort> ports;
889 namePort.
name =
b.getStringAttr(
"name");
890 namePort.
type = hw::StringType::get(
b.getContext());
891 namePort.
dir = hw::ModulePort::Direction::Input;
892 ports.push_back(namePort);
896 fdPort.
name =
b.getStringAttr(
"fd");
897 fdPort.
type =
b.getIntegerType(32);
898 fdPort.
dir = hw::ModulePort::Direction::Output;
899 ports.push_back(fdPort);
902 auto moduleType = hw::ModuleType::get(
b.getContext(), ports);
904 SmallVector<NamedAttribute> perArgumentsAttr;
905 perArgumentsAttr.push_back(
906 {sv::FuncOp::getExplicitlyReturnedAttrName(),
b.getUnitAttr()});
908 SmallVector<Attribute> argumentAttr = {
909 DictionaryAttr::get(
b.getContext(), {}),
910 DictionaryAttr::get(
b.getContext(), perArgumentsAttr)};
913 auto func = sv::FuncOp::create(
915 "__circt_lib_logging::FileDescriptor::get", moduleType,
918 {b.getDictionaryAttr({}),
b.getDictionaryAttr(perArgumentsAttr)}),
924 b.getStringAttr(
"__circt_lib_logging::FileDescriptor::get"));
927 sv::MacroDeclOp::create(b,
"__CIRCT_LIB_LOGGING");
929 emit::FragmentOp::create(b,
"CIRCT_LIB_LOGGING_FRAGMENT", [&] {
930 emitGuard(
"SYNTHESIS", [&]() {
931 emitGuard(
"__CIRCT_LIB_LOGGING", [&]() {
932 sv::VerbatimOp::create(b, R
"(// CIRCT Logging Library
933package __circt_lib_logging;
934 class FileDescriptor;
935 static int global_id [string];
936 static function int get(string name);
937 if (global_id.exists(name) == 32'h0) begin
938 global_id[name] = $fopen(name, "w");
939 if (global_id[name] == 32'h0)
940 $error("Failed to open file %s", name);
942 return global_id[name];
948 sv::MacroDefOp::create(b, "__CIRCT_LIB_LOGGING",
"");
954 if (state.usedPrintf) {
955 sv::MacroDeclOp::create(b,
"PRINTF_COND");
956 sv::MacroDeclOp::create(b,
"PRINTF_COND_");
957 emit::FragmentOp::create(b,
"PRINTF_COND_FRAGMENT", [&] {
958 sv::VerbatimOp::create(
959 b,
"\n// Users can define 'PRINTF_COND' to add an extra gate to "
961 emitGuard(
"PRINTF_COND_", [&]() {
962 emitGuardedDefine(
"PRINTF_COND",
"PRINTF_COND_",
"(`PRINTF_COND)",
"1");
967 if (state.usedAssertVerboseCond) {
968 sv::MacroDeclOp::create(b,
"ASSERT_VERBOSE_COND");
969 sv::MacroDeclOp::create(b,
"ASSERT_VERBOSE_COND_");
970 emit::FragmentOp::create(b,
"ASSERT_VERBOSE_COND_FRAGMENT", [&] {
971 sv::VerbatimOp::create(
972 b,
"\n// Users can define 'ASSERT_VERBOSE_COND' to add an extra "
973 "gate to assert error printing.");
974 emitGuard(
"ASSERT_VERBOSE_COND_", [&]() {
975 emitGuardedDefine(
"ASSERT_VERBOSE_COND",
"ASSERT_VERBOSE_COND_",
976 "(`ASSERT_VERBOSE_COND)",
"1");
981 if (state.usedStopCond) {
982 sv::MacroDeclOp::create(b,
"STOP_COND");
983 sv::MacroDeclOp::create(b,
"STOP_COND_");
984 emit::FragmentOp::create(b,
"STOP_COND_FRAGMENT", [&] {
985 sv::VerbatimOp::create(
986 b,
"\n// Users can define 'STOP_COND' to add an extra gate "
987 "to stop conditions.");
988 emitGuard(
"STOP_COND_", [&]() {
989 emitGuardedDefine(
"STOP_COND",
"STOP_COND_",
"(`STOP_COND)",
"1");
996FIRRTLModuleLowering::lowerPorts(ArrayRef<PortInfo> firrtlPorts,
997 SmallVectorImpl<hw::PortInfo> &ports,
998 Operation *moduleOp, StringRef moduleName,
1000 ports.reserve(firrtlPorts.size());
1002 size_t numResults = 0;
1003 for (
auto e :
llvm::enumerate(firrtlPorts)) {
1005 size_t portNo = e.index();
1010 if (firrtlPort.
sym.size() > 1 ||
1011 (firrtlPort.
sym.size() == 1 && !firrtlPort.
sym.getSymName()))
1012 return emitError(firrtlPort.
loc)
1013 <<
"cannot lower aggregate port " << firrtlPort.
name
1014 <<
" with field sensitive symbols, HW dialect does not support "
1015 "per field symbols yet.";
1016 hwPort.
setSym(firrtlPort.
sym, moduleOp->getContext());
1018 if (hadDontTouch && !hwPort.
getSym()) {
1019 if (hwPort.
type.isInteger(0)) {
1020 if (enableAnnotationWarning) {
1021 mlir::emitWarning(firrtlPort.
loc)
1022 <<
"zero width port " << hwPort.
name
1023 <<
" has dontTouch annotation, removing anyway";
1029 hw::InnerSymAttr::get(StringAttr::get(
1030 moduleOp->getContext(),
1031 Twine(
"__") + moduleName + Twine(
"__DONTTOUCH__") +
1032 Twine(portNo) + Twine(
"__") + firrtlPort.
name.strref())),
1033 moduleOp->getContext());
1038 moduleOp->emitError(
"cannot lower this port type to HW");
1044 if (hwPort.
type.isInteger(0)) {
1045 auto sym = hwPort.
getSym();
1046 if (sym && !sym.empty()) {
1047 return mlir::emitError(firrtlPort.
loc)
1048 <<
"zero width port " << hwPort.
name
1049 <<
" is referenced by name [" << sym
1050 <<
"] (e.g. in an XMR) but must be removed";
1057 hwPort.
dir = hw::ModulePort::Direction::Output;
1058 hwPort.
argNum = numResults++;
1059 }
else if (firrtlPort.
isInput()) {
1060 hwPort.
dir = hw::ModulePort::Direction::Input;
1061 hwPort.
argNum = numArgs++;
1065 hwPort.
type = hw::InOutType::get(hwPort.
type);
1066 hwPort.
dir = hw::ModulePort::Direction::InOut;
1067 hwPort.
argNum = numArgs++;
1069 hwPort.
loc = firrtlPort.
loc;
1070 ports.push_back(hwPort);
1080 auto params = llvm::map_range(module.getParameters(), [](Attribute a) {
1081 return cast<ParamDeclAttr>(a);
1086 Builder builder(module);
1091 SmallVector<Attribute> newParams;
1092 for (
const ParamDeclAttr &entry : params) {
1093 auto name = entry.getName();
1094 auto type = entry.getType();
1095 auto value = ignoreValues ? Attribute() : entry.getValue();
1097 hw::ParamDeclAttr::get(builder.getContext(), name, type, value);
1098 newParams.push_back(paramAttr);
1100 return builder.getArrayAttr(newParams);
1103bool FIRRTLModuleLowering::handleForceNameAnnos(
1106 bool failed =
false;
1109 if (!anno.
isClass(forceNameAnnoClass))
1112 auto sym = anno.
getMember<FlatSymbolRefAttr>(
"circt.nonlocal");
1119 auto diag = oldModule.emitOpError()
1120 <<
"contains a '" << forceNameAnnoClass
1121 <<
"' that is not a non-local annotation";
1122 diag.attachNote() <<
"the erroneous annotation is '" << anno.
getDict()
1133 auto diag = oldModule.emitOpError()
1134 <<
"contains a '" << forceNameAnnoClass
1135 <<
"' whose non-local symbol, '" << sym
1136 <<
"' does not exist in the circuit";
1137 diag.attachNote() <<
"the erroneous annotation is '" << anno.
getDict();
1150 cast<hw::InnerRefAttr>(nla.getNamepath().getValue().take_back(2)[0]);
1152 {{inst.getModule(), inst.getName()}, anno.
getMember(
"name")});
1153 if (!inserted.second &&
1154 (anno.
getMember(
"name") != (inserted.first->second))) {
1155 auto diag = oldModule.emitError()
1156 <<
"contained multiple '" << forceNameAnnoClass
1157 <<
"' with different names: " << inserted.first->second
1158 <<
" was not " << anno.
getMember(
"name");
1159 diag.attachNote() <<
"the erroneous annotation is '" << anno.
getDict()
1170 FExtModuleOp oldModule, Block *topLevelModule,
1181 SmallVector<PortInfo> firrtlPorts = oldModule.getPorts();
1182 SmallVector<hw::PortInfo, 8> ports;
1183 if (failed(lowerPorts(firrtlPorts, ports, oldModule, oldModule.getName(),
1188 StringRef verilogName;
1189 if (
auto defName = oldModule.getDefname())
1190 verilogName = defName.value();
1192 verilogName = oldModule.getName();
1194 auto builder = OpBuilder::atBlockEnd(topLevelModule);
1196 auto filesAttr = verbatimAnno.
getMember<ArrayAttr>(
"files");
1197 if (!filesAttr || filesAttr.empty()) {
1198 oldModule->emitError(
"VerbatimBlackBoxAnno missing or empty files array");
1203 auto primaryFile = cast<DictionaryAttr>(filesAttr[0]);
1204 auto primaryFileContent = primaryFile.getAs<StringAttr>(
"content");
1205 auto primaryOutputFile = primaryFile.getAs<StringAttr>(
"output_file");
1207 if (!primaryFileContent || !primaryOutputFile) {
1208 oldModule->emitError(
"VerbatimBlackBoxAnno file missing fields");
1212 auto primaryOutputFileAttr = hw::OutputFileAttr::getFromFilename(
1213 builder.getContext(), primaryOutputFile.getValue());
1215 auto primaryFileName = llvm::sys::path::filename(primaryOutputFile);
1216 auto verbatimSource =
loweringState.getVerbatimSourceForFile(primaryFileName);
1219 SmallVector<Attribute> additionalFiles;
1223 for (
size_t i = 1; i < filesAttr.size(); ++i) {
1224 auto file = cast<DictionaryAttr>(filesAttr[i]);
1225 auto content = file.getAs<StringAttr>(
"content");
1226 auto outputFile = file.getAs<StringAttr>(
"output_file");
1227 auto fileName = llvm::sys::path::filename(outputFile);
1229 if (!(content && outputFile)) {
1230 oldModule->emitError(
"VerbatimBlackBoxAnno file missing fields");
1238 auto fileSymbolName = circuitNamespace.newName(fileName);
1239 emitFile = emit::FileOp::create(builder, oldModule.getLoc(),
1240 outputFile.getValue(), fileSymbolName);
1241 builder.setInsertionPointToStart(&
emitFile.getBodyRegion().front());
1242 emit::VerbatimOp::create(builder, oldModule.getLoc(), content);
1243 builder.setInsertionPointAfter(
emitFile);
1246 auto ext = llvm::sys::path::extension(outputFile.getValue());
1247 bool excludeFromFileList = (ext ==
".h" || ext ==
".vh" || ext ==
".svh");
1248 auto outputFileAttr = hw::OutputFileAttr::getFromFilename(
1249 builder.getContext(), outputFile.getValue(), excludeFromFileList);
1250 emitFile->setAttr(
"output_file", outputFileAttr);
1254 additionalFiles.push_back(FlatSymbolRefAttr::get(
emitFile));
1260 parameters = builder.getArrayAttr({});
1262 if (!verbatimSource) {
1263 verbatimSource = sv::SVVerbatimSourceOp::create(
1264 builder, oldModule.getLoc(),
1265 circuitNamespace.newName(primaryFileName.str()),
1266 primaryFileContent.getValue(), primaryOutputFileAttr, parameters,
1267 additionalFiles.empty() ?
nullptr
1268 : builder.getArrayAttr(additionalFiles),
1269 builder.getStringAttr(verilogName));
1271 SymbolTable::setSymbolVisibility(
1272 verbatimSource, SymbolTable::getSymbolVisibility(oldModule));
1274 loweringState.registerVerbatimSource(primaryFileName, verbatimSource);
1277 return verbatimSource;
1281FIRRTLModuleLowering::lowerExtModule(FExtModuleOp oldModule,
1282 Block *topLevelModule,
1284 if (
auto verbatimMod =
1285 lowerVerbatimExtModule(oldModule, topLevelModule,
loweringState))
1291 SmallVector<PortInfo> firrtlPorts = oldModule.getPorts();
1292 SmallVector<hw::PortInfo, 8> ports;
1293 if (failed(lowerPorts(firrtlPorts, ports, oldModule, oldModule.getName(),
1297 StringRef verilogName;
1298 if (
auto defName = oldModule.getDefname())
1299 verilogName = defName.value();
1302 auto builder = OpBuilder::atBlockEnd(topLevelModule);
1303 auto nameAttr = builder.getStringAttr(oldModule.getName());
1308 auto newModule = hw::HWModuleExternOp::create(
1309 builder, oldModule.getLoc(), nameAttr, ports, verilogName, parameters);
1310 SymbolTable::setSymbolVisibility(newModule,
1311 SymbolTable::getSymbolVisibility(oldModule));
1313 bool hasOutputPort =
1314 llvm::any_of(firrtlPorts, [&](
auto p) {
return p.isOutput(); });
1315 if (!hasOutputPort &&
1317 internalVerifBlackBoxAnnoClass) &&
1319 newModule->setAttr(
"firrtl.extract.cover.extra", builder.getUnitAttr());
1322 if (
auto extReqs = oldModule.getExternalRequirements();
1323 extReqs && !extReqs.empty())
1324 newModule->setAttr(
"circt.external_requirements", extReqs);
1329 loweringState.processRemainingAnnotations(oldModule, annos);
1334 FExtModuleOp oldModule, Block *topLevelModule,
1339 auto verbatimSource =
1340 getVerbatimSourceForExtModule(oldModule, topLevelModule,
loweringState);
1342 if (!verbatimSource)
1345 SmallVector<PortInfo> firrtlPorts = oldModule.getPorts();
1346 SmallVector<hw::PortInfo, 8> ports;
1347 if (failed(lowerPorts(firrtlPorts, ports, oldModule, oldModule.getName(),
1351 StringRef verilogName;
1352 if (
auto defName = oldModule.getDefname())
1353 verilogName = defName.value();
1355 auto builder = OpBuilder::atBlockEnd(topLevelModule);
1357 auto newModule = sv::SVVerbatimModuleOp::create(
1360 builder.getStringAttr(oldModule.getName()),
1362 FlatSymbolRefAttr::get(verbatimSource),
1363 parameters ? parameters : builder.getArrayAttr({}),
1364 verilogName.empty() ? StringAttr{}
1365 : builder.getStringAttr(verilogName));
1367 SymbolTable::setSymbolVisibility(newModule,
1368 SymbolTable::getSymbolVisibility(oldModule));
1370 bool hasOutputPort =
1371 llvm::any_of(firrtlPorts, [&](
auto p) {
return p.isOutput(); });
1372 if (!hasOutputPort &&
1374 internalVerifBlackBoxAnnoClass) &&
1376 newModule->setAttr(
"firrtl.extract.cover.extra", builder.getUnitAttr());
1379 if (
auto extReqs = oldModule.getExternalRequirements();
1380 extReqs && !extReqs.empty())
1381 newModule->setAttr(
"circt.external_requirements", extReqs);
1386 loweringState.processRemainingAnnotations(oldModule, annos);
1391FIRRTLModuleLowering::lowerMemModule(FMemModuleOp oldModule,
1392 Block *topLevelModule,
1395 SmallVector<PortInfo> firrtlPorts = oldModule.getPorts();
1396 SmallVector<hw::PortInfo, 8> ports;
1397 if (failed(lowerPorts(firrtlPorts, ports, oldModule, oldModule.getName(),
1402 auto builder = OpBuilder::atBlockEnd(topLevelModule);
1403 auto newModule = hw::HWModuleExternOp::create(
1404 builder, oldModule.getLoc(), oldModule.getModuleNameAttr(), ports,
1405 oldModule.getModuleNameAttr());
1413FIRRTLModuleLowering::lowerModule(FModuleOp oldModule, Block *topLevelModule,
1416 SmallVector<PortInfo> firrtlPorts = oldModule.getPorts();
1417 SmallVector<hw::PortInfo, 8> ports;
1418 if (failed(lowerPorts(firrtlPorts, ports, oldModule, oldModule.getName(),
1423 auto builder = OpBuilder::atBlockEnd(topLevelModule);
1424 auto nameAttr = builder.getStringAttr(oldModule.getName());
1426 hw::HWModuleOp::create(builder, oldModule.getLoc(), nameAttr, ports);
1428 if (
auto comment = oldModule->getAttrOfType<StringAttr>(
"comment"))
1429 newModule.setCommentAttr(comment);
1432 SmallVector<StringRef, 13> attrNames = {
1433 "annotations",
"convention",
"layers",
1434 "portNames",
"sym_name",
"portDirections",
1435 "portTypes",
"portAnnotations",
"portSymbols",
1436 "portLocations",
"parameters", SymbolTable::getVisibilityAttrName(),
1439 DenseSet<StringRef> attrSet(attrNames.begin(), attrNames.end());
1440 SmallVector<NamedAttribute> newAttrs(newModule->getAttrs());
1442 llvm::make_filter_range(oldModule->getAttrs(), [&](auto namedAttr) {
1443 return !attrSet.count(namedAttr.getName()) &&
1444 !newModule->getAttrDictionary().contains(namedAttr.getName());
1446 newAttrs.push_back(i);
1448 newModule->setAttrs(newAttrs);
1452 SymbolTable::setSymbolVisibility(newModule,
1453 SymbolTable::getSymbolVisibility(oldModule));
1459 newModule->setAttr(
"firrtl.extract.cover.extra", builder.getUnitAttr());
1463 if (
auto testBenchDir =
loweringState.getTestBenchDirectory())
1465 if (!newModule->hasAttr(
"output_file"))
1466 newModule->setAttr(
"output_file", testBenchDir);
1467 newModule->setAttr(
"firrtl.extract.do_not_extract",
1468 builder.getUnitAttr());
1469 newModule.setCommentAttr(
1470 builder.getStringAttr(
"VCS coverage exclude_file"));
1476 loweringState.processRemainingAnnotations(oldModule, annos);
1485 Operation *insertPoint) {
1486 if (!value.hasOneUse())
1489 auto attach = dyn_cast<AttachOp>(*value.user_begin());
1490 if (!attach || attach.getNumOperands() != 2)
1494 auto loweredType =
lowerType(value.getType());
1495 if (loweredType.isInteger(0))
1500 auto attachedValue = attach.getOperand(attach.getOperand(0) == value);
1501 auto *op = attachedValue.getDefiningOp();
1502 if (op && op->getBlock() == insertPoint->getBlock() &&
1503 !op->isBeforeInBlock(insertPoint))
1508 ImplicitLocOpBuilder builder(insertPoint->getLoc(), insertPoint);
1524 if (type_isa<AnalogType>(flipValue.getType()))
1527 Operation *connectOp =
nullptr;
1528 for (
auto &use : flipValue.getUses()) {
1531 if (use.getOperandNumber() != 0)
1533 if (!isa<ConnectOp, MatchingConnectOp>(use.getOwner()))
1539 connectOp = use.getOwner();
1549 loweringState.lowerType(flipValue.getType(), flipValue.getLoc());
1550 if (loweredType.isInteger(0))
1555 ImplicitLocOpBuilder builder(insertPoint->getLoc(), insertPoint);
1557 auto connectSrc = connectOp->getOperand(1);
1560 if (!isa<FIRRTLType>(connectSrc.getType())) {
1566 if (!type_cast<FIRRTLBaseType>(connectSrc.getType()).isPassive())
1568 mlir::UnrealizedConversionCastOp::create(
1570 type_cast<FIRRTLBaseType>(connectSrc.getType()).getPassiveType(),
1576 auto destTy = type_cast<FIRRTLBaseType>(flipValue.getType()).getPassiveType();
1578 if (destTy != connectSrc.getType() &&
1579 (isa<BaseTypeAliasType>(connectSrc.getType()) ||
1580 isa<BaseTypeAliasType>(destTy))) {
1582 builder.createOrFold<BitCastOp>(flipValue.getType(), connectSrc);
1584 if (!destTy.isGround()) {
1586 if (destTy != type_cast<FIRRTLType>(connectSrc.getType()))
1588 }
else if (destTy.getBitWidthOrSentinel() !=
1589 type_cast<FIRRTLBaseType>(connectSrc.getType())
1590 .getBitWidthOrSentinel()) {
1593 auto destWidth = destTy.getBitWidthOrSentinel();
1594 assert(destWidth != -1 &&
"must know integer widths");
1595 connectSrc = builder.createOrFold<PadPrimOp>(destTy, connectSrc, destWidth);
1607 SmallVector<SubfieldOp> accesses;
1608 for (
auto *op : structValue.getUsers()) {
1609 assert(isa<SubfieldOp>(op));
1610 auto fieldAccess = cast<SubfieldOp>(op);
1612 fieldAccess.getInput().getType().base().getElementIndex(field);
1613 if (elemIndex && *elemIndex == fieldAccess.getFieldIndex())
1614 accesses.push_back(fieldAccess);
1622LogicalResult FIRRTLModuleLowering::lowerModulePortsAndMoveBody(
1625 ImplicitLocOpBuilder bodyBuilder(oldModule.getLoc(), newModule.getBody());
1632 bodyBuilder.setInsertionPoint(cursor);
1635 SmallVector<PortInfo> firrtlPorts = oldModule.getPorts();
1636 assert(oldModule.getBody().getNumArguments() == firrtlPorts.size() &&
1637 "port count mismatch");
1639 SmallVector<Value, 4> outputs;
1642 auto *outputOp = newModule.getBodyBlock()->getTerminator();
1643 ImplicitLocOpBuilder outputBuilder(oldModule.getLoc(), outputOp);
1645 unsigned nextHWInputArg = 0;
1646 int hwPortIndex = -1;
1647 for (
auto [firrtlPortIndex, port] :
llvm::enumerate(firrtlPorts)) {
1649 auto oldArg = oldModule.getBody().getArgument(firrtlPortIndex);
1652 type_isa<FIRRTLBaseType>(port.type) &&
1653 type_cast<FIRRTLBaseType>(port.type).getBitWidthOrSentinel() == 0;
1657 if (!port.isOutput() && !isZeroWidth) {
1660 Value newArg = newModule.getBody().getArgument(nextHWInputArg++);
1666 oldArg.replaceAllUsesWith(newArg);
1672 if (isZeroWidth && port.isInput()) {
1674 WireOp::create(bodyBuilder, port.type,
1675 "." + port.getName().str() +
".0width_input")
1677 oldArg.replaceAllUsesWith(newArg);
1685 outputs.push_back(value);
1686 assert(oldArg.use_empty() &&
"should have removed all uses of oldArg");
1692 auto newArg = WireOp::create(bodyBuilder, port.type,
1693 "." + port.getName().str() +
".output");
1696 oldArg.replaceAllUsesWith(newArg.getResult());
1699 auto resultHWType =
loweringState.lowerType(port.type, port.loc);
1700 if (!resultHWType.isInteger(0)) {
1703 outputs.push_back(output);
1706 if (
auto sym = newModule.getPort(hwPortIndex).getSym()) {
1707 newArg.setInnerSymAttr(sym);
1708 newModule.setPortSymbolAttr(hwPortIndex, {});
1714 outputOp->setOperands(outputs);
1717 auto &oldBlockInstList = oldModule.getBodyBlock()->getOperations();
1718 auto &newBlockInstList = newModule.getBodyBlock()->getOperations();
1719 newBlockInstList.splice(Block::iterator(cursor), oldBlockInstList,
1720 oldBlockInstList.begin(), oldBlockInstList.end());
1731FIRRTLModuleLowering::lowerFormalBody(verif::FormalOp newOp,
1733 auto builder = OpBuilder::atBlockEnd(&newOp.getBody().front());
1738 auto oldOp = cast<FormalOp>(
loweringState.getOldModule(newOp));
1739 auto moduleName = oldOp.getModuleNameAttr().getAttr();
1740 auto oldModule = cast<FModuleOp>(
1741 loweringState.getInstanceGraph().lookup(moduleName)->getModule());
1742 auto newModule = cast<hw::HWModuleOp>(
loweringState.getNewModule(oldModule));
1745 SmallVector<Value> symbolicInputs;
1746 for (
auto arg : newModule.getBody().getArguments())
1747 symbolicInputs.push_back(
verif::SymbolicValueOp::create(
1748 builder, arg.
getLoc(), arg.getType(),
1752 hw::InstanceOp::create(builder, newOp.getLoc(), newModule,
1753 newModule.getNameAttr(), symbolicInputs);
1760FIRRTLModuleLowering::lowerSimulationBody(verif::SimulationOp newOp,
1762 auto builder = OpBuilder::atBlockEnd(newOp.getBody());
1765 auto oldOp = cast<SimulationOp>(
loweringState.getOldModule(newOp));
1766 auto moduleName = oldOp.getModuleNameAttr().getAttr();
1767 auto oldModule = cast<FModuleLike>(
1768 *
loweringState.getInstanceGraph().lookup(moduleName)->getModule());
1770 cast<hw::HWModuleLike>(
loweringState.getNewModule(oldModule));
1774 SmallVector<Value> inputs(newOp.getBody()->args_begin(),
1775 newOp.getBody()->args_end());
1776 auto instOp = hw::InstanceOp::create(builder, newOp.getLoc(), newModule,
1777 newModule.getNameAttr(), inputs);
1778 verif::YieldOp::create(builder, newOp.getLoc(), instOp.getResults());
1788struct FIRRTLLowering :
public FIRRTLVisitor<FIRRTLLowering, LogicalResult> {
1790 FIRRTLLowering(
hw::HWModuleOp module, CircuitLoweringState &circuitState)
1791 : theModule(module), circuitState(circuitState),
1792 builder(module.
getLoc(), module.getContext()), moduleNamespace(module),
1793 backedgeBuilder(builder, module.
getLoc()) {}
1795 LogicalResult
run();
1798 Value getOrCreateClockConstant(seq::ClockConst clock);
1799 Value getOrCreateIntConstant(
const APInt &value);
1800 Value getOrCreateIntConstant(
unsigned numBits, uint64_t val,
1801 bool isSigned =
false) {
1802 return getOrCreateIntConstant(APInt(numBits, val, isSigned));
1804 Attribute getOrCreateAggregateConstantAttribute(Attribute value, Type type);
1805 Value getOrCreateXConstant(
unsigned numBits);
1806 Value getOrCreateZConstant(Type type);
1807 Value getPossiblyInoutLoweredValue(Value value);
1808 Value getLoweredValue(Value value);
1809 Value getLoweredNonClockValue(Value value);
1810 Value getLoweredAndExtendedValue(Value value, Type destType);
1811 Value getLoweredAndExtOrTruncValue(Value value, Type destType);
1812 LogicalResult setLowering(Value orig, Value result);
1813 LogicalResult setPossiblyFoldedLowering(Value orig, Value result);
1814 template <
typename ResultOpType,
typename... CtorArgTypes>
1815 LogicalResult setLoweringTo(Operation *orig, CtorArgTypes... args);
1816 template <
typename ResultOpType,
typename... CtorArgTypes>
1817 LogicalResult setLoweringToLTL(Operation *orig, CtorArgTypes... args);
1818 Backedge createBackedge(Location loc, Type type);
1819 Backedge createBackedge(Value orig, Type type);
1820 bool updateIfBackedge(Value dest, Value src);
1823 bool requiresInnerSymbol(hw::InnerSymbolOpInterface op) {
1828 if (
auto forceable = dyn_cast<Forceable>(op.getOperation()))
1829 if (forceable.isForceable())
1837 hw::InnerSymAttr lowerInnerSymbol(hw::InnerSymbolOpInterface op) {
1838 auto attr = op.getInnerSymAttr();
1842 if (requiresInnerSymbol(op))
1844 op.getContext(), attr, 0,
1852 LogicalResult prepareInstanceOperands(ArrayRef<PortInfo> portInfo,
1853 Operation *instanceOp,
1854 SmallVectorImpl<Value> &inputOperands);
1856 void runWithInsertionPointAtEndOfBlock(
const std::function<
void(
void)> &fn,
1860 Value getReadValue(Value v);
1862 Value getNonClockValue(Value v);
1864 void addToAlwaysBlock(sv::EventControl clockEdge, Value clock,
1865 sv::ResetType resetStyle, sv::EventControl resetEdge,
1866 Value reset,
const std::function<
void(
void)> &body = {},
1867 const std::function<void(
void)> &resetBody = {});
1868 void addToAlwaysBlock(Value clock,
1869 const std::function<
void(
void)> &body = {}) {
1870 addToAlwaysBlock(sv::EventControl::AtPosEdge, clock, sv::ResetType(),
1871 sv::EventControl(), Value(), body,
1872 std::function<
void(
void)>());
1875 LogicalResult emitGuards(Location loc, ArrayRef<Attribute> guards,
1876 std::function<
void(
void)>
emit);
1877 void addToIfDefBlock(StringRef cond, std::function<
void(
void)> thenCtor,
1878 std::function<
void(
void)> elseCtor = {});
1879 void addToInitialBlock(std::function<
void(
void)> body);
1880 void addIfProceduralBlock(Value cond, std::function<
void(
void)> thenCtor,
1881 std::function<
void(
void)> elseCtor = {});
1882 Value getExtOrTruncAggregateValue(Value array,
FIRRTLBaseType sourceType,
1884 bool allowTruncate);
1885 Value createArrayIndexing(Value array, Value index);
1886 Value createValueWithMuxAnnotation(Operation *op,
bool isMux2);
1888 using FIRRTLVisitor<FIRRTLLowering, LogicalResult>::visitExpr;
1889 using FIRRTLVisitor<FIRRTLLowering, LogicalResult>::visitDecl;
1890 using FIRRTLVisitor<FIRRTLLowering, LogicalResult>::visitStmt;
1893 enum UnloweredOpResult { AlreadyLowered, NowLowered, LoweringFailure };
1894 UnloweredOpResult handleUnloweredOp(Operation *op);
1895 LogicalResult visitExpr(ConstantOp op);
1896 LogicalResult visitExpr(SpecialConstantOp op);
1897 LogicalResult visitExpr(SubindexOp op);
1898 LogicalResult visitExpr(SubaccessOp op);
1899 LogicalResult visitExpr(SubfieldOp op);
1900 LogicalResult visitExpr(VectorCreateOp op);
1901 LogicalResult visitExpr(BundleCreateOp op);
1902 LogicalResult visitExpr(FEnumCreateOp op);
1903 LogicalResult visitExpr(AggregateConstantOp op);
1904 LogicalResult visitExpr(IsTagOp op);
1905 LogicalResult visitExpr(SubtagOp op);
1906 LogicalResult visitExpr(TagExtractOp op);
1909 if (
auto castOp = dyn_cast<mlir::UnrealizedConversionCastOp>(op))
1910 return visitUnrealizedConversionCast(castOp);
1915 LogicalResult visitDecl(WireOp op);
1916 LogicalResult visitDecl(NodeOp op);
1917 LogicalResult visitDecl(RegOp op);
1918 LogicalResult visitDecl(RegResetOp op);
1919 LogicalResult visitDecl(MemOp op);
1920 LogicalResult visitDecl(InstanceOp oldInstance);
1921 LogicalResult visitDecl(InstanceChoiceOp oldInstanceChoice);
1922 LogicalResult visitDecl(VerbatimWireOp op);
1923 LogicalResult visitDecl(ContractOp op);
1926 LogicalResult lowerNoopCast(Operation *op);
1927 LogicalResult visitExpr(AsSIntPrimOp op);
1928 LogicalResult visitExpr(AsUIntPrimOp op);
1929 LogicalResult visitExpr(AsClockPrimOp op);
1930 LogicalResult visitExpr(AsAsyncResetPrimOp op) {
return lowerNoopCast(op); }
1932 LogicalResult visitExpr(HWStructCastOp op);
1933 LogicalResult visitExpr(BitCastOp op);
1935 visitUnrealizedConversionCast(mlir::UnrealizedConversionCastOp op);
1936 LogicalResult visitExpr(CvtPrimOp op);
1937 LogicalResult visitExpr(NotPrimOp op);
1938 LogicalResult visitExpr(NegPrimOp op);
1939 LogicalResult visitExpr(PadPrimOp op);
1940 LogicalResult visitExpr(XorRPrimOp op);
1941 LogicalResult visitExpr(AndRPrimOp op);
1942 LogicalResult visitExpr(OrRPrimOp op);
1945 template <
typename ResultUnsignedOpType,
1946 typename ResultSignedOpType = ResultUnsignedOpType>
1947 LogicalResult lowerBinOp(Operation *op);
1948 template <
typename ResultOpType>
1949 LogicalResult lowerBinOpToVariadic(Operation *op);
1951 template <
typename ResultOpType>
1952 LogicalResult lowerElementwiseLogicalOp(Operation *op);
1954 LogicalResult lowerCmpOp(Operation *op, ICmpPredicate signedOp,
1955 ICmpPredicate unsignedOp);
1956 template <
typename SignedOp,
typename Un
signedOp>
1957 LogicalResult lowerDivLikeOp(Operation *op);
1959 LogicalResult visitExpr(CatPrimOp op);
1961 LogicalResult visitExpr(AndPrimOp op) {
1962 return lowerBinOpToVariadic<comb::AndOp>(op);
1964 LogicalResult visitExpr(OrPrimOp op) {
1965 return lowerBinOpToVariadic<comb::OrOp>(op);
1967 LogicalResult visitExpr(XorPrimOp op) {
1968 return lowerBinOpToVariadic<comb::XorOp>(op);
1970 LogicalResult visitExpr(ElementwiseOrPrimOp op) {
1971 return lowerElementwiseLogicalOp<comb::OrOp>(op);
1973 LogicalResult visitExpr(ElementwiseAndPrimOp op) {
1974 return lowerElementwiseLogicalOp<comb::AndOp>(op);
1976 LogicalResult visitExpr(ElementwiseXorPrimOp op) {
1977 return lowerElementwiseLogicalOp<comb::XorOp>(op);
1979 LogicalResult visitExpr(AddPrimOp op) {
1980 return lowerBinOpToVariadic<comb::AddOp>(op);
1982 LogicalResult visitExpr(EQPrimOp op) {
1983 return lowerCmpOp(op, ICmpPredicate::eq, ICmpPredicate::eq);
1985 LogicalResult visitExpr(NEQPrimOp op) {
1986 return lowerCmpOp(op, ICmpPredicate::ne, ICmpPredicate::ne);
1988 LogicalResult visitExpr(LTPrimOp op) {
1989 return lowerCmpOp(op, ICmpPredicate::slt, ICmpPredicate::ult);
1991 LogicalResult visitExpr(LEQPrimOp op) {
1992 return lowerCmpOp(op, ICmpPredicate::sle, ICmpPredicate::ule);
1994 LogicalResult visitExpr(GTPrimOp op) {
1995 return lowerCmpOp(op, ICmpPredicate::sgt, ICmpPredicate::ugt);
1997 LogicalResult visitExpr(GEQPrimOp op) {
1998 return lowerCmpOp(op, ICmpPredicate::sge, ICmpPredicate::uge);
2001 LogicalResult visitExpr(SubPrimOp op) {
return lowerBinOp<comb::SubOp>(op); }
2002 LogicalResult visitExpr(MulPrimOp op) {
2003 return lowerBinOpToVariadic<comb::MulOp>(op);
2005 LogicalResult visitExpr(DivPrimOp op) {
2006 return lowerDivLikeOp<comb::DivSOp, comb::DivUOp>(op);
2008 LogicalResult visitExpr(RemPrimOp op) {
2009 return lowerDivLikeOp<comb::ModSOp, comb::ModUOp>(op);
2013 LogicalResult visitExpr(IsXIntrinsicOp op);
2014 LogicalResult visitExpr(PlusArgsTestIntrinsicOp op);
2015 LogicalResult visitExpr(PlusArgsValueIntrinsicOp op);
2016 LogicalResult visitStmt(FPGAProbeIntrinsicOp op);
2017 LogicalResult visitExpr(ClockInverterIntrinsicOp op);
2018 LogicalResult visitExpr(ClockDividerIntrinsicOp op);
2019 LogicalResult visitExpr(SizeOfIntrinsicOp op);
2020 LogicalResult visitExpr(ClockGateIntrinsicOp op);
2021 LogicalResult visitExpr(LTLAndIntrinsicOp op);
2022 LogicalResult visitExpr(LTLOrIntrinsicOp op);
2023 LogicalResult visitExpr(LTLIntersectIntrinsicOp op);
2024 LogicalResult visitExpr(LTLDelayIntrinsicOp op);
2025 LogicalResult visitExpr(LTLConcatIntrinsicOp op);
2026 LogicalResult visitExpr(LTLRepeatIntrinsicOp op);
2027 LogicalResult visitExpr(LTLGoToRepeatIntrinsicOp op);
2028 LogicalResult visitExpr(LTLNonConsecutiveRepeatIntrinsicOp op);
2029 LogicalResult visitExpr(LTLNotIntrinsicOp op);
2030 LogicalResult visitExpr(LTLImplicationIntrinsicOp op);
2031 LogicalResult visitExpr(LTLUntilIntrinsicOp op);
2032 LogicalResult visitExpr(LTLEventuallyIntrinsicOp op);
2033 LogicalResult visitExpr(LTLPastIntrinsicOp op);
2034 LogicalResult visitExpr(LTLClockIntrinsicOp op);
2036 template <
typename TargetOp,
typename IntrinsicOp>
2037 LogicalResult lowerVerifIntrinsicOp(IntrinsicOp op);
2038 LogicalResult visitStmt(VerifAssertIntrinsicOp op);
2039 LogicalResult visitStmt(VerifAssumeIntrinsicOp op);
2040 LogicalResult visitStmt(VerifCoverIntrinsicOp op);
2041 LogicalResult visitStmt(VerifRequireIntrinsicOp op);
2042 LogicalResult visitStmt(VerifEnsureIntrinsicOp op);
2043 LogicalResult visitExpr(HasBeenResetIntrinsicOp op);
2044 LogicalResult visitStmt(UnclockedAssumeIntrinsicOp op);
2047 LogicalResult visitExpr(BitsPrimOp op);
2048 LogicalResult visitExpr(InvalidValueOp op);
2049 LogicalResult visitExpr(HeadPrimOp op);
2050 LogicalResult visitExpr(ShlPrimOp op);
2051 LogicalResult visitExpr(ShrPrimOp op);
2052 LogicalResult visitExpr(DShlPrimOp op) {
2053 return lowerDivLikeOp<comb::ShlOp, comb::ShlOp>(op);
2055 LogicalResult visitExpr(DShrPrimOp op) {
2056 return lowerDivLikeOp<comb::ShrSOp, comb::ShrUOp>(op);
2058 LogicalResult visitExpr(DShlwPrimOp op) {
2059 return lowerDivLikeOp<comb::ShlOp, comb::ShlOp>(op);
2061 LogicalResult visitExpr(TailPrimOp op);
2062 LogicalResult visitExpr(MuxPrimOp op);
2063 LogicalResult visitExpr(Mux2CellIntrinsicOp op);
2064 LogicalResult visitExpr(Mux4CellIntrinsicOp op);
2065 LogicalResult visitExpr(MultibitMuxOp op);
2066 LogicalResult visitExpr(VerbatimExprOp op);
2067 LogicalResult visitExpr(XMRRefOp op);
2068 LogicalResult visitExpr(XMRDerefOp op);
2071 LogicalResult visitExpr(TimeOp op);
2072 LogicalResult visitExpr(HierarchicalModuleNameOp op);
2075 LogicalResult lowerVerificationStatement(
2076 Operation *op, StringRef labelPrefix, Value clock, Value predicate,
2077 Value enable, StringAttr messageAttr, ValueRange operands,
2078 StringAttr nameAttr,
bool isConcurrent, EventControl eventControl);
2079 LogicalResult lowerVerificationStatementToCore(
2080 Operation *op, StringRef labelPrefix, Value clock, Value predicate,
2081 Value enable, StringAttr nameAttr, EventControl eventControl);
2083 LogicalResult visitStmt(SkipOp op);
2085 FailureOr<bool> lowerConnect(Value dest, Value srcVal);
2086 LogicalResult visitStmt(ConnectOp op);
2087 LogicalResult visitStmt(MatchingConnectOp op);
2088 LogicalResult visitStmt(ForceOp op);
2090 std::optional<Value> getLoweredFmtOperand(Value operand);
2091 LogicalResult loweredFmtOperands(ValueRange operands,
2092 SmallVectorImpl<Value> &loweredOperands);
2093 FailureOr<Value> lowerSimFormatString(StringRef originalFormatString,
2094 ValueRange operands);
2095 FailureOr<Value> callFileDescriptorLib(
const FileDescriptorInfo &info);
2099 LogicalResult lowerStatementWithFd(
2100 const FileDescriptorInfo &fileDescriptorInfo, Value clock, Value cond,
2101 const std::function<LogicalResult(Value)> &fn,
bool usePrintfCond);
2105 LogicalResult visitPrintfLike(T op,
2106 const FileDescriptorInfo &fileDescriptorInfo,
2107 bool usePrintfCond);
2108 LogicalResult visitStmt(PrintFOp op);
2109 LogicalResult visitStmt(FPrintFOp op);
2110 LogicalResult visitStmt(FFlushOp op);
2111 LogicalResult visitStmt(StopOp op);
2112 LogicalResult visitStmt(AssertOp op);
2113 LogicalResult visitStmt(AssumeOp op);
2114 LogicalResult visitStmt(CoverOp op);
2115 LogicalResult visitStmt(AttachOp op);
2116 LogicalResult visitStmt(RefForceOp op);
2117 LogicalResult visitStmt(RefForceInitialOp op);
2118 LogicalResult visitStmt(RefReleaseOp op);
2119 LogicalResult visitStmt(RefReleaseInitialOp op);
2120 LogicalResult visitStmt(BindOp op);
2122 FailureOr<Value> lowerSubindex(SubindexOp op, Value input);
2123 FailureOr<Value> lowerSubaccess(SubaccessOp op, Value input);
2124 FailureOr<Value> lowerSubfield(SubfieldOp op, Value input);
2126 LogicalResult fixupLTLOps();
2129 return circuitState.lowerType(type, builder.getLoc());
2137 CircuitLoweringState &circuitState;
2140 ImplicitLocOpBuilder builder;
2145 DenseMap<Value, Value> valueMapping;
2149 DenseMap<Value, Value> fromClockMapping;
2153 DenseMap<Attribute, Value> hwConstantMap;
2154 DenseMap<std::pair<Attribute, Type>, Attribute> hwAggregateConstantMap;
2158 DenseMap<unsigned, Value> hwConstantXMap;
2159 DenseMap<Type, Value> hwConstantZMap;
2165 DenseMap<Value, Value> readInOutCreated;
2168 DenseMap<StringAttr, sv::RegOp> fileNameToFileDescriptor;
2172 using AlwaysKeyType = std::tuple<
Block *, sv::EventControl, Value,
2173 sv::ResetType, sv::EventControl, Value>;
2197 DenseSet<Operation *> maybeUnusedValues;
2199 void maybeUnused(Operation *op) { maybeUnusedValues.insert(op); }
2200 void maybeUnused(Value value) {
2201 if (
auto *op = value.getDefiningOp())
2213 SetVector<Operation *> ltlOpFixupWorklist;
2218 SmallVector<std::pair<Block::iterator, Block::iterator>> worklist;
2220 void addToWorklist(Block &block) {
2221 worklist.push_back({block.begin(), block.end()});
2223 void addToWorklist(Region ®ion) {
2224 for (
auto &block :
llvm::reverse(region))
2225 addToWorklist(block);
2236LogicalResult FIRRTLModuleLowering::lowerFileBody(emit::FileOp fileOp) {
2237 OpBuilder
b(&getContext());
2238 fileOp->walk([&](Operation *op) {
2239 if (
auto bindOp = dyn_cast<BindOp>(op)) {
2240 b.setInsertionPointAfter(bindOp);
2241 sv::BindOp::create(b, bindOp.getLoc(), bindOp.getInstanceAttr());
2249FIRRTLModuleLowering::lowerBody(Operation *op,
2251 if (
auto moduleOp = dyn_cast<hw::HWModuleOp>(op))
2253 if (
auto formalOp = dyn_cast<verif::FormalOp>(op))
2255 if (
auto simulationOp = dyn_cast<verif::SimulationOp>(op))
2257 if (
auto fileOp = dyn_cast<emit::FileOp>(op))
2258 return lowerFileBody(fileOp);
2263LogicalResult FIRRTLLowering::run() {
2266 for (
auto arg : theModule.
getBodyBlock()->getArguments())
2267 if (failed(setLowering(arg, arg)))
2274 addToWorklist(theModule.getBody());
2275 SmallVector<Operation *, 16> opsToRemove;
2277 while (!worklist.empty()) {
2278 auto &[opsIt, opsEnd] = worklist.back();
2279 if (opsIt == opsEnd) {
2280 worklist.pop_back();
2283 Operation *op = &*opsIt++;
2285 builder.setInsertionPoint(op);
2286 builder.setLoc(op->getLoc());
2287 auto done = succeeded(dispatchVisitor(op));
2288 circuitState.processRemainingAnnotations(op,
AnnotationSet(op));
2290 opsToRemove.push_back(op);
2292 switch (handleUnloweredOp(op)) {
2293 case AlreadyLowered:
2296 opsToRemove.push_back(op);
2298 case LoweringFailure:
2310 for (
auto &[backedge, value] : backedges) {
2311 SmallVector<Location> driverLocs;
2317 if (backedge == value) {
2318 Location edgeLoc = backedge.getLoc();
2319 if (driverLocs.empty()) {
2320 mlir::emitError(edgeLoc,
"sink does not have a driver");
2322 auto diag = mlir::emitError(edgeLoc,
"sink in combinational loop");
2323 for (
auto loc : driverLocs)
2324 diag.attachNote(loc) <<
"through driver here";
2330 auto *it = backedges.find(value);
2331 if (it == backedges.end())
2334 driverLocs.push_back(value.getLoc());
2337 if (
auto *defOp = backedge.getDefiningOp())
2338 maybeUnusedValues.erase(defOp);
2339 backedge.replaceAllUsesWith(value);
2347 while (!opsToRemove.empty()) {
2348 auto *op = opsToRemove.pop_back_val();
2353 for (
auto result : op->getResults()) {
2357 auto builder = OpBuilder::atBlockBegin(theModule.getBodyBlock());
2359 builder.getIntegerType(0), 0);
2360 maybeUnusedValues.insert(zeroI0);
2362 result.replaceAllUsesWith(zeroI0);
2365 if (!op->use_empty()) {
2366 auto d = op->emitOpError(
2367 "still has uses; should remove ops in reverse order of visitation");
2368 SmallPtrSet<Operation *, 2> visited;
2369 for (
auto *user : op->getUsers())
2370 if (visited.insert(user).second)
2372 <<
"used by " << user->
getName() <<
" op";
2375 maybeUnusedValues.erase(op);
2380 while (!maybeUnusedValues.empty()) {
2381 auto it = maybeUnusedValues.begin();
2383 maybeUnusedValues.erase(it);
2384 if (!isOpTriviallyDead(op))
2386 for (
auto operand : op->getOperands())
2387 if (auto *defOp = operand.getDefiningOp())
2388 maybeUnusedValues.insert(defOp);
2394 if (failed(fixupLTLOps()))
2405Value FIRRTLLowering::getOrCreateClockConstant(seq::ClockConst clock) {
2406 auto attr = seq::ClockConstAttr::get(theModule.getContext(), clock);
2408 auto &entry = hwConstantMap[attr];
2412 OpBuilder entryBuilder(&theModule.getBodyBlock()->front());
2413 entry = seq::ConstClockOp::create(entryBuilder, builder.getLoc(), attr);
2419Value FIRRTLLowering::getOrCreateIntConstant(
const APInt &value) {
2420 auto attr = builder.getIntegerAttr(
2421 builder.getIntegerType(value.getBitWidth()), value);
2423 auto &entry = hwConstantMap[attr];
2427 OpBuilder entryBuilder(&theModule.getBodyBlock()->front());
2434Attribute FIRRTLLowering::getOrCreateAggregateConstantAttribute(Attribute value,
2437 if (hw::type_isa<IntegerType>(type))
2438 return builder.getIntegerAttr(type, cast<IntegerAttr>(value).getValue());
2440 auto cache = hwAggregateConstantMap.lookup({value, type});
2445 SmallVector<Attribute> values;
2446 for (
auto e :
llvm::enumerate(cast<ArrayAttr>(value))) {
2448 if (
auto array = hw::type_dyn_cast<hw::ArrayType>(type))
2449 subType = array.getElementType();
2450 else if (
auto structType = hw::type_dyn_cast<hw::StructType>(type))
2451 subType = structType.getElements()[e.index()].type;
2453 assert(
false &&
"type must be either array or struct");
2455 values.push_back(getOrCreateAggregateConstantAttribute(e.value(), subType));
2459 if (hw::type_isa<hw::ArrayType>(type))
2460 std::reverse(values.begin(), values.end());
2462 auto &entry = hwAggregateConstantMap[{value, type}];
2463 entry = builder.getArrayAttr(values);
2471 const std::function<LogicalResult()> &fn) {
2472 assert(failedOperand &&
"Should be called on the failed operand");
2480Value FIRRTLLowering::getOrCreateXConstant(
unsigned numBits) {
2482 auto &entry = hwConstantXMap[numBits];
2486 OpBuilder entryBuilder(&theModule.getBodyBlock()->front());
2487 entry = sv::ConstantXOp::create(entryBuilder, builder.getLoc(),
2488 entryBuilder.getIntegerType(numBits));
2492Value FIRRTLLowering::getOrCreateZConstant(Type type) {
2493 auto &entry = hwConstantZMap[type];
2495 OpBuilder entryBuilder(&theModule.getBodyBlock()->front());
2496 entry = sv::ConstantZOp::create(entryBuilder, builder.getLoc(), type);
2505Value FIRRTLLowering::getPossiblyInoutLoweredValue(Value value) {
2507 if (
auto lowering = valueMapping.lookup(value)) {
2508 assert(!isa<FIRRTLType>(lowering.getType()) &&
2509 "Lowered value should be a non-FIRRTL value");
2518Value FIRRTLLowering::getLoweredValue(Value value) {
2519 auto result = getPossiblyInoutLoweredValue(value);
2525 if (isa<hw::InOutType>(result.getType()))
2526 return getReadValue(result);
2532Value FIRRTLLowering::getLoweredNonClockValue(Value value) {
2533 auto result = getLoweredValue(value);
2537 if (hw::type_isa<seq::ClockType>(result.getType()))
2538 return getNonClockValue(result);
2546Value FIRRTLLowering::getExtOrTruncAggregateValue(Value array,
2549 bool allowTruncate) {
2550 SmallVector<Value> resultBuffer;
2555 auto srcWidth = firrtl::type_cast<IntType>(sourceType).getWidthOrSentinel();
2556 auto destWidth = firrtl::type_cast<IntType>(destType).getWidthOrSentinel();
2557 auto resultType = builder.getIntegerType(destWidth);
2559 if (srcWidth == destWidth)
2562 if (srcWidth > destWidth) {
2566 builder.emitError(
"operand should not be a truncation");
2570 if (firrtl::type_cast<IntType>(sourceType).isSigned())
2571 return comb::createOrFoldSExt(builder, value, resultType);
2572 auto zero = getOrCreateIntConstant(destWidth - srcWidth, 0);
2580 return TypeSwitch<FIRRTLBaseType, LogicalResult>(srcType)
2581 .Case<FVectorType>([&](
auto srcVectorType) {
2582 auto destVectorType = firrtl::type_cast<FVectorType>(destType);
2583 unsigned size = resultBuffer.size();
2584 unsigned indexWidth =
2586 for (
size_t i = 0, e = std::min(srcVectorType.getNumElements(),
2587 destVectorType.getNumElements());
2589 auto iIdx = getOrCreateIntConstant(indexWidth, i);
2591 if (failed(recurse(arrayIndex, srcVectorType.getElementType(),
2592 destVectorType.getElementType())))
2595 SmallVector<Value> temp(resultBuffer.begin() + size,
2596 resultBuffer.end());
2598 resultBuffer.resize(size);
2599 resultBuffer.push_back(array);
2602 .Case<BundleType>([&](BundleType srcStructType) {
2603 auto destStructType = firrtl::type_cast<BundleType>(destType);
2604 unsigned size = resultBuffer.size();
2607 if (destStructType.getNumElements() != srcStructType.getNumElements())
2610 for (
auto elem :
llvm::enumerate(destStructType)) {
2611 auto structExtract =
2613 if (failed(recurse(structExtract,
2614 srcStructType.getElementType(elem.index()),
2615 destStructType.getElementType(elem.index()))))
2618 SmallVector<Value> temp(resultBuffer.begin() + size,
2619 resultBuffer.end());
2622 resultBuffer.resize(size);
2623 resultBuffer.push_back(newStruct);
2626 .Case<IntType>([&](
auto) {
2627 if (
auto result = cast(src, srcType, destType)) {
2628 resultBuffer.push_back(result);
2633 .Default([&](
auto) {
return failure(); });
2636 if (failed(recurse(array, sourceType, destType)))
2639 assert(resultBuffer.size() == 1 &&
2640 "resultBuffer must only contain a result array if `success` is true");
2641 return resultBuffer[0];
2649Value FIRRTLLowering::getLoweredAndExtendedValue(Value src, Type target) {
2650 auto srcType = cast<FIRRTLBaseType>(src.getType());
2651 auto dstType = cast<FIRRTLBaseType>(target);
2652 auto loweredSrc = getLoweredValue(src);
2655 auto dstWidth = dstType.getBitWidthOrSentinel();
2671 return getOrCreateIntConstant(dstWidth, 0);
2674 auto loweredSrcType = loweredSrc.getType();
2675 auto loweredDstType =
lowerType(dstType);
2678 if (loweredSrcType == loweredDstType)
2682 if (dstWidth == srcType.getBitWidthOrSentinel()) {
2684 if (loweredSrcType != loweredDstType &&
2685 (isa<hw::TypeAliasType>(loweredSrcType) ||
2686 isa<hw::TypeAliasType>(loweredDstType))) {
2687 return builder.createOrFold<
hw::BitcastOp>(loweredDstType, loweredSrc);
2692 if (isa<hw::ArrayType, hw::StructType>(loweredSrcType))
2693 return getExtOrTruncAggregateValue(loweredSrc, srcType, dstType,
2696 if (isa<seq::ClockType>(loweredSrcType)) {
2697 builder.emitError(
"cannot use clock type as an integer");
2701 auto intSourceType = dyn_cast<IntegerType>(loweredSrcType);
2702 if (!intSourceType) {
2703 builder.emitError(
"operand of type ")
2704 << loweredSrcType <<
" cannot be used as an integer";
2708 auto loweredSrcWidth = intSourceType.getWidth();
2709 if (loweredSrcWidth ==
unsigned(dstWidth))
2712 if (loweredSrcWidth >
unsigned(dstWidth)) {
2713 builder.emitError(
"operand should not be a truncation");
2718 auto valueFIRType = type_cast<FIRRTLBaseType>(src.getType()).getPassiveType();
2719 if (type_cast<IntType>(valueFIRType).isSigned())
2720 return comb::createOrFoldSExt(builder, loweredSrc, loweredDstType);
2722 auto zero = getOrCreateIntConstant(dstWidth - loweredSrcWidth, 0);
2731Value FIRRTLLowering::getLoweredAndExtOrTruncValue(Value value, Type destType) {
2732 assert(type_isa<FIRRTLBaseType>(value.getType()) &&
2733 type_isa<FIRRTLBaseType>(destType) &&
2734 "input/output value should be FIRRTL");
2737 auto destWidth = type_cast<FIRRTLBaseType>(destType).getBitWidthOrSentinel();
2738 if (destWidth == -1)
2741 auto result = getLoweredValue(value);
2753 return getOrCreateIntConstant(destWidth, 0);
2757 if (isa<hw::ArrayType, hw::StructType>(result.getType())) {
2759 if (destType == value.getType())
2762 return getExtOrTruncAggregateValue(
2763 result, type_cast<FIRRTLBaseType>(value.getType()),
2764 type_cast<FIRRTLBaseType>(destType),
2768 auto srcWidth = type_cast<IntegerType>(result.getType()).getWidth();
2769 if (srcWidth ==
unsigned(destWidth))
2775 if (srcWidth >
unsigned(destWidth)) {
2776 auto resultType = builder.getIntegerType(destWidth);
2780 auto resultType = builder.getIntegerType(destWidth);
2784 type_cast<FIRRTLBaseType>(value.getType()).getPassiveType();
2785 if (type_cast<IntType>(valueFIRType).isSigned())
2786 return comb::createOrFoldSExt(builder, result, resultType);
2788 auto zero = getOrCreateIntConstant(destWidth - srcWidth, 0);
2802std::optional<Value> FIRRTLLowering::getLoweredFmtOperand(Value operand) {
2804 if (type_isa<FStringType>(operand.getType())) {
2805 if (isa<TimeOp>(operand.getDefiningOp()))
2806 return sv::TimeOp::create(builder);
2807 if (isa<HierarchicalModuleNameOp>(operand.getDefiningOp()))
2811 auto loweredValue = getLoweredValue(operand);
2812 if (!loweredValue) {
2816 loweredValue = getOrCreateIntConstant(1, 0);
2821 if (
auto intTy = firrtl::type_cast<IntType>(operand.getType()))
2822 if (intTy.isSigned())
2823 loweredValue = sv::SystemFunctionOp::create(
2824 builder, loweredValue.getType(),
"signed", loweredValue);
2826 return loweredValue;
2830FIRRTLLowering::loweredFmtOperands(mlir::ValueRange operands,
2831 SmallVectorImpl<Value> &loweredOperands) {
2832 for (
auto operand : operands) {
2833 std::optional<Value> loweredValue = getLoweredFmtOperand(operand);
2838 loweredOperands.push_back(*loweredValue);
2844FIRRTLLowering::lowerSimFormatString(StringRef originalFormatString,
2845 ValueRange operands) {
2846 SmallVector<Value> fragments;
2848 auto emitLiteral = [&](StringRef text) {
2850 fragments.push_back(sim::FormatLiteralOp::create(builder, text));
2853 auto emitIntFormat = [&](Value operand,
char specifier,
2854 IntegerAttr widthAttr) -> FailureOr<Value> {
2856 if (type_isa<ClockType>(operand.getType()))
2857 loweredValue = getLoweredNonClockValue(operand);
2859 loweredValue = getLoweredValue(operand);
2860 if (!loweredValue) {
2863 loweredValue = getOrCreateIntConstant(1, 0);
2866 if (!mlir::isa<IntegerType>(loweredValue.getType())) {
2867 emitError(builder.getLoc(),
"lower-to-core requires integer printf "
2869 << specifier <<
"'";
2873 switch (specifier) {
2875 return sim::FormatBinOp::create(builder, loweredValue,
2876 builder.getBoolAttr(
false),
2877 builder.getI8IntegerAttr(
'0'), widthAttr)
2880 UnitAttr signedAttr;
2881 if (
auto intTy = dyn_cast<IntType>(operand.getType());
2882 intTy && intTy.isSigned())
2883 signedAttr = builder.getUnitAttr();
2884 return sim::FormatDecOp::create(
2885 builder, loweredValue, builder.getBoolAttr(
false),
2886 builder.getI8IntegerAttr(
' '), widthAttr, signedAttr)
2890 return sim::FormatHexOp::create(builder, loweredValue,
2891 builder.getBoolAttr(
false),
2892 builder.getBoolAttr(
false),
2893 builder.getI8IntegerAttr(
'0'), widthAttr)
2896 return sim::FormatCharOp::create(builder, loweredValue).getResult();
2898 llvm_unreachable(
"unsupported FIRRTL format specifier");
2902 SmallString<32> literal;
2903 for (
size_t i = 0, e = originalFormatString.size(), subIdx = 0; i != e; ++i) {
2904 char c = originalFormatString[i];
2907 emitLiteral(literal);
2910 SmallString<6> width;
2911 c = originalFormatString[++i];
2914 c = originalFormatString[++i];
2917 IntegerAttr widthAttr;
2918 if (!width.empty()) {
2919 unsigned widthValue;
2920 if (StringRef(width).getAsInteger(10, widthValue)) {
2921 emitError(builder.getLoc(),
"invalid FIRRTL printf width");
2924 widthAttr = builder.getI32IntegerAttr(widthValue);
2928 if (!width.empty()) {
2929 emitError(builder.getLoc(),
2930 "literal percents ('%%') may not specify a width");
2933 literal.push_back(
'%');
2937 if (operands.size() <= subIdx) {
2938 emitError(builder.getLoc(),
"not enough operands for printf format");
2942 if (c ==
'c' && widthAttr) {
2943 emitError(builder.getLoc(),
"ASCII character format specifiers ('%c') "
2944 "may not specify a width");
2953 auto fragment = emitIntFormat(operands[subIdx++], c, widthAttr);
2954 if (failed(fragment))
2956 fragments.push_back(*fragment);
2960 emitError(builder.getLoc(),
"unknown printf substitution '%")
2961 << width << c <<
"'";
2967 if (originalFormatString.slice(i, i + 4) !=
"{{}}") {
2968 literal.push_back(c);
2972 emitLiteral(literal);
2975 if (operands.size() <= subIdx) {
2976 emitError(builder.getLoc(),
"not enough operands for printf format");
2980 auto substitution = operands[subIdx++];
2981 if (!type_isa<FStringType>(substitution.getType())) {
2982 emitError(builder.getLoc(),
"expected fstring operand for '{{}}' "
2988 TypeSwitch<Operation *, LogicalResult>(substitution.getDefiningOp())
2989 .template Case<HierarchicalModuleNameOp>([&](
auto) {
2990 fragments.push_back(sim::FormatHierPathOp::create(
2994 .
template Case<TimeOp>([&](
auto) {
2995 emitError(builder.getLoc(),
"lower-to-core does not support "
2996 "{{SimulationTime}} in printf");
2999 .Default([&](
auto) {
3000 emitError(builder.getLoc(),
"has a substitution with "
3003 .attachNote(substitution.getLoc())
3004 <<
"op with an unimplemented lowering is here";
3013 literal.push_back(c);
3018 emitLiteral(literal);
3019 if (fragments.empty())
3020 return sim::FormatLiteralOp::create(builder,
"").getResult();
3021 if (fragments.size() == 1)
3022 return fragments.front();
3023 return sim::FormatStringConcatOp::create(builder, fragments).getResult();
3026LogicalResult FIRRTLLowering::lowerStatementWithFd(
3027 const FileDescriptorInfo &fileDescriptor, Value clock, Value cond,
3028 const std::function<LogicalResult(Value)> &fn,
bool usePrintfCond) {
3030 bool failed =
false;
3031 circuitState.addMacroDecl(builder.getStringAttr(
"SYNTHESIS"));
3032 addToIfDefBlock(
"SYNTHESIS", std::function<
void()>(), [&]() {
3033 addToAlwaysBlock(clock, [&]() {
3036 circuitState.usedPrintf =
true;
3038 circuitState.addFragment(theModule,
"PRINTF_COND_FRAGMENT");
3041 Value ifCond = cond;
3042 if (usePrintfCond) {
3044 sv::MacroRefExprOp::create(builder, cond.getType(),
"PRINTF_COND_");
3045 ifCond = builder.createOrFold<
comb::AndOp>(ifCond, cond,
true);
3048 addIfProceduralBlock(ifCond, [&]() {
3052 if (fileDescriptor.isDefaultFd()) {
3054 fd = hw::ConstantOp::create(builder, APInt(32, 0x80000002));
3057 auto fdOrError = callFileDescriptorLib(fileDescriptor);
3058 if (llvm::failed(fdOrError)) {
3064 failed = llvm::failed(fn(fd));
3068 return failure(failed);
3072FIRRTLLowering::callFileDescriptorLib(
const FileDescriptorInfo &info) {
3073 circuitState.usedFileDescriptorLib =
true;
3074 circuitState.addFragment(theModule,
"CIRCT_LIB_LOGGING_FRAGMENT");
3077 if (
info.isSubstitutionRequired()) {
3078 SmallVector<Value> fileNameOperands;
3079 if (failed(loweredFmtOperands(
info.getSubstitutions(), fileNameOperands)))
3082 fileName = sv::SFormatFOp::create(builder,
info.getOutputFileFormat(),
3087 fileName = sv::ConstantStrOp::create(builder,
info.getOutputFileFormat())
3091 return sv::FuncCallProceduralOp::create(
3092 builder, mlir::TypeRange{builder.getIntegerType(32)},
3093 builder.getStringAttr(
"__circt_lib_logging::FileDescriptor::get"),
3094 ValueRange{fileName})
3104LogicalResult FIRRTLLowering::setLowering(Value orig, Value result) {
3105 if (
auto origType = dyn_cast<FIRRTLType>(orig.getType())) {
3106 assert((!result || !type_isa<FIRRTLType>(result.getType())) &&
3107 "Lowering didn't turn a FIRRTL value into a non-FIRRTL value");
3111 auto srcWidth = baseType.getPassiveType().getBitWidthOrSentinel();
3114 if (srcWidth != -1) {
3116 assert((srcWidth != 0) &&
3117 "Lowering produced value for zero width source");
3119 assert((srcWidth == 0) &&
3120 "Lowering produced null value but source wasn't zero width");
3124 assert(result &&
"Lowering of foreign type produced null value");
3127 auto &slot = valueMapping[orig];
3128 assert(!slot &&
"value lowered multiple times");
3135LogicalResult FIRRTLLowering::setPossiblyFoldedLowering(Value orig,
3139 if (
auto cst = dyn_cast_or_null<hw::ConstantOp>(result.getDefiningOp())) {
3140 auto &entry = hwConstantMap[cst.getValueAttr()];
3151 cst->moveBefore(&theModule.getBodyBlock()->front());
3155 return setLowering(orig, result);
3160template <
typename ResultOpType,
typename... CtorArgTypes>
3161LogicalResult FIRRTLLowering::setLoweringTo(Operation *orig,
3162 CtorArgTypes... args) {
3163 auto result = builder.createOrFold<ResultOpType>(args...);
3164 if (
auto *op = result.getDefiningOp())
3166 return setPossiblyFoldedLowering(orig->getResult(0), result);
3173template <
typename ResultOpType,
typename... CtorArgTypes>
3174LogicalResult FIRRTLLowering::setLoweringToLTL(Operation *orig,
3175 CtorArgTypes... args) {
3176 auto result = builder.createOrFold<ResultOpType>(args...);
3177 if (
auto *op = result.getDefiningOp())
3178 ltlOpFixupWorklist.insert(op);
3179 return setPossiblyFoldedLowering(orig->getResult(0), result);
3188Backedge FIRRTLLowering::createBackedge(Location loc, Type type) {
3189 auto backedge = backedgeBuilder.
get(type, loc);
3190 backedges.insert({backedge, backedge});
3198Backedge FIRRTLLowering::createBackedge(Value orig, Type type) {
3199 auto backedge = createBackedge(orig.getLoc(), type);
3200 (void)setLowering(orig, backedge);
3206bool FIRRTLLowering::updateIfBackedge(Value dest, Value src) {
3207 auto backedgeIt = backedges.find(dest);
3208 if (backedgeIt == backedges.end())
3210 backedgeIt->second = src;
3218void FIRRTLLowering::runWithInsertionPointAtEndOfBlock(
3219 const std::function<
void(
void)> &fn, Region ®ion) {
3223 auto oldIP = builder.saveInsertionPoint();
3225 builder.setInsertionPointToEnd(®ion.front());
3227 builder.restoreInsertionPoint(oldIP);
3231Value FIRRTLLowering::getReadValue(Value v) {
3232 Value result = readInOutCreated.lookup(v);
3238 auto oldIP = builder.saveInsertionPoint();
3239 if (
auto *vOp = v.getDefiningOp()) {
3240 builder.setInsertionPointAfter(vOp);
3244 builder.setInsertionPoint(&theModule.getBodyBlock()->front());
3249 if (
auto arrayIndexInout = v.getDefiningOp<sv::ArrayIndexInOutOp>()) {
3250 result = getReadValue(arrayIndexInout.getInput());
3252 arrayIndexInout.getIndex());
3257 builder.restoreInsertionPoint(oldIP);
3258 readInOutCreated.insert({v, result});
3262Value FIRRTLLowering::getNonClockValue(Value v) {
3263 auto it = fromClockMapping.try_emplace(v, Value{});
3265 ImplicitLocOpBuilder builder(v.getLoc(), v.getContext());
3266 builder.setInsertionPointAfterValue(v);
3267 it.first->second = seq::FromClockOp::create(builder, v);
3269 return it.first->second;
3272void FIRRTLLowering::addToAlwaysBlock(
3273 sv::EventControl clockEdge, Value clock, sv::ResetType resetStyle,
3274 sv::EventControl resetEdge, Value reset,
3275 const std::function<
void(
void)> &body,
3276 const std::function<
void(
void)> &resetBody) {
3277 AlwaysKeyType key{builder.getBlock(), clockEdge, clock,
3278 resetStyle, resetEdge, reset};
3279 sv::AlwaysOp alwaysOp;
3280 sv::IfOp insideIfOp;
3281 std::tie(alwaysOp, insideIfOp) = alwaysBlocks.lookup(key);
3285 assert(resetStyle != sv::ResetType::NoReset);
3298 auto createIfOp = [&]() {
3301 insideIfOp = sv::IfOp::create(
3302 builder, reset, [] {}, [] {});
3304 if (resetStyle == sv::ResetType::AsyncReset) {
3305 sv::EventControl events[] = {clockEdge, resetEdge};
3306 Value clocks[] = {clock, reset};
3308 alwaysOp = sv::AlwaysOp::create(builder, events, clocks, [&]() {
3309 if (resetEdge == sv::EventControl::AtNegEdge)
3310 llvm_unreachable(
"negative edge for reset is not expected");
3314 alwaysOp = sv::AlwaysOp::create(builder, clockEdge, clock, createIfOp);
3318 alwaysOp = sv::AlwaysOp::create(builder, clockEdge, clock);
3319 insideIfOp =
nullptr;
3321 alwaysBlocks[key] = {alwaysOp, insideIfOp};
3325 assert(insideIfOp &&
"reset body must be initialized before");
3326 runWithInsertionPointAtEndOfBlock(resetBody, insideIfOp.getThenRegion());
3327 runWithInsertionPointAtEndOfBlock(body, insideIfOp.getElseRegion());
3329 runWithInsertionPointAtEndOfBlock(body, alwaysOp.getBody());
3335 alwaysOp->moveBefore(builder.getInsertionBlock(),
3336 builder.getInsertionPoint());
3339LogicalResult FIRRTLLowering::emitGuards(Location loc,
3340 ArrayRef<Attribute> guards,
3341 std::function<
void(
void)>
emit) {
3342 if (guards.empty()) {
3346 auto guard = dyn_cast<StringAttr>(guards[0]);
3348 return mlir::emitError(loc,
3349 "elements in `guards` array must be `StringAttr`");
3352 circuitState.addMacroDecl(builder.getStringAttr(guard.getValue()));
3353 LogicalResult result = LogicalResult::failure();
3354 addToIfDefBlock(guard.getValue(), [&]() {
3355 result = emitGuards(loc, guards.drop_front(), emit);
3360void FIRRTLLowering::addToIfDefBlock(StringRef cond,
3361 std::function<
void(
void)> thenCtor,
3362 std::function<
void(
void)> elseCtor) {
3363 auto condAttr = builder.getStringAttr(cond);
3364 auto op = ifdefBlocks.lookup({builder.getBlock(), condAttr});
3366 runWithInsertionPointAtEndOfBlock(thenCtor, op.getThenRegion());
3367 runWithInsertionPointAtEndOfBlock(elseCtor, op.getElseRegion());
3372 op->moveBefore(builder.getInsertionBlock(), builder.getInsertionPoint());
3374 ifdefBlocks[{builder.getBlock(), condAttr}] =
3375 sv::IfDefOp::create(builder, condAttr, thenCtor, elseCtor);
3379void FIRRTLLowering::addToInitialBlock(std::function<
void(
void)> body) {
3380 auto op = initialBlocks.lookup(builder.getBlock());
3382 runWithInsertionPointAtEndOfBlock(body, op.getBody());
3387 op->moveBefore(builder.getInsertionBlock(), builder.getInsertionPoint());
3389 initialBlocks[builder.getBlock()] = sv::InitialOp::create(builder, body);
3393void FIRRTLLowering::addIfProceduralBlock(Value cond,
3394 std::function<
void(
void)> thenCtor,
3395 std::function<
void(
void)> elseCtor) {
3398 auto insertIt = builder.getInsertionPoint();
3399 if (insertIt != builder.getBlock()->begin())
3400 if (
auto ifOp = dyn_cast<sv::IfOp>(*--insertIt)) {
3401 if (ifOp.getCond() == cond) {
3402 runWithInsertionPointAtEndOfBlock(thenCtor, ifOp.getThenRegion());
3403 runWithInsertionPointAtEndOfBlock(elseCtor, ifOp.getElseRegion());
3408 sv::IfOp::create(builder, cond, thenCtor, elseCtor);
3420FIRRTLLowering::UnloweredOpResult
3421FIRRTLLowering::handleUnloweredOp(Operation *op) {
3423 if (!op->getRegions().empty() &&
3424 isa_and_nonnull<FIRRTLDialect>(op->getDialect())) {
3425 op->emitOpError(
"must explicitly handle its regions");
3426 return LoweringFailure;
3433 if (!isa_and_nonnull<FIRRTLDialect>(op->getDialect())) {
3435 for (
auto ®ion : op->getRegions())
3436 addToWorklist(region);
3437 for (
auto &operand : op->getOpOperands())
3438 if (auto lowered = getPossiblyInoutLoweredValue(operand.
get()))
3439 operand.set(lowered);
3440 for (
auto result : op->getResults())
3441 (void)setLowering(result, result);
3442 return AlreadyLowered;
3454 if (op->getNumResults() == 1) {
3455 auto resultType = op->getResult(0).getType();
3456 if (type_isa<FIRRTLBaseType>(resultType) &&
3458 (
isExpression(op) || isa<mlir::UnrealizedConversionCastOp>(op))) {
3460 (void)setLowering(op->getResult(0), Value());
3464 op->emitOpError(
"LowerToHW couldn't handle this operation");
3465 return LoweringFailure;
3468LogicalResult FIRRTLLowering::visitExpr(ConstantOp op) {
3471 return setLowering(op, Value());
3473 return setLowering(op, getOrCreateIntConstant(op.getValue()));
3476LogicalResult FIRRTLLowering::visitExpr(SpecialConstantOp op) {
3478 if (isa<ClockType>(op.getType())) {
3479 cst = getOrCreateClockConstant(op.getValue() ? seq::ClockConst::High
3480 :
seq::ClockConst::Low);
3482 cst = getOrCreateIntConstant(APInt( 1, op.getValue()));
3484 return setLowering(op, cst);
3487FailureOr<Value> FIRRTLLowering::lowerSubindex(SubindexOp op, Value input) {
3488 auto iIdx = getOrCreateIntConstant(
3490 firrtl::type_cast<FVectorType>(op.getInput().getType())
3497 if (isa<sv::InOutType>(input.getType()))
3498 result = builder.createOrFold<sv::ArrayIndexInOutOp>(input, iIdx);
3501 if (
auto *definingOp = result.getDefiningOp())
3506FailureOr<Value> FIRRTLLowering::lowerSubaccess(SubaccessOp op, Value input) {
3507 Value valueIdx = getLoweredAndExtOrTruncValue(
3509 UIntType::get(op->getContext(),
3511 firrtl::type_cast<FVectorType>(op.getInput().getType())
3512 .getNumElements())));
3514 op->emitError() <<
"input lowering failed";
3521 if (isa<sv::InOutType>(input.getType()))
3522 result = builder.createOrFold<sv::ArrayIndexInOutOp>(input, valueIdx);
3524 result = createArrayIndexing(input, valueIdx);
3525 if (
auto *definingOp = result.getDefiningOp())
3530FailureOr<Value> FIRRTLLowering::lowerSubfield(SubfieldOp op, Value input) {
3531 auto resultType =
lowerType(op->getResult(0).getType());
3532 if (!resultType || !input) {
3533 op->emitError() <<
"subfield type lowering failed";
3539 auto field = firrtl::type_cast<BundleType>(op.getInput().getType())
3540 .getElementName(op.getFieldIndex());
3542 if (isa<sv::InOutType>(input.getType()))
3543 result = builder.createOrFold<sv::StructFieldInOutOp>(input, field);
3546 if (
auto *definingOp = result.getDefiningOp())
3551LogicalResult FIRRTLLowering::visitExpr(SubindexOp op) {
3553 return setLowering(op, Value());
3555 auto input = getPossiblyInoutLoweredValue(op.getInput());
3557 return op.emitError() <<
"input lowering failed";
3559 auto result = lowerSubindex(op, input);
3562 return setLowering(op, *result);
3565LogicalResult FIRRTLLowering::visitExpr(SubaccessOp op) {
3567 return setLowering(op, Value());
3569 auto input = getPossiblyInoutLoweredValue(op.getInput());
3571 return op.emitError() <<
"input lowering failed";
3573 auto result = lowerSubaccess(op, input);
3576 return setLowering(op, *result);
3579LogicalResult FIRRTLLowering::visitExpr(SubfieldOp op) {
3582 if (getLoweredValue(op) || !op.getInput())
3586 return setLowering(op, Value());
3588 auto input = getPossiblyInoutLoweredValue(op.getInput());
3590 return op.emitError() <<
"input lowering failed";
3592 auto result = lowerSubfield(op, input);
3595 return setLowering(op, *result);
3598LogicalResult FIRRTLLowering::visitExpr(VectorCreateOp op) {
3599 auto resultType =
lowerType(op.getResult().getType());
3600 SmallVector<Value> operands;
3602 for (
auto oper :
llvm::reverse(op.getOperands())) {
3603 auto val = getLoweredValue(oper);
3606 operands.push_back(val);
3608 return setLoweringTo<hw::ArrayCreateOp>(op, resultType, operands);
3611LogicalResult FIRRTLLowering::visitExpr(BundleCreateOp op) {
3612 auto resultType =
lowerType(op.getResult().getType());
3613 SmallVector<Value> operands;
3614 for (
auto oper : op.getOperands()) {
3615 auto val = getLoweredValue(oper);
3618 operands.push_back(val);
3620 return setLoweringTo<hw::StructCreateOp>(op, resultType, operands);
3623LogicalResult FIRRTLLowering::visitExpr(FEnumCreateOp op) {
3626 return setLowering(op, Value());
3628 auto input = getLoweredValue(op.getInput());
3629 auto tagName = op.getFieldNameAttr();
3630 auto oldType = op.getType().base();
3632 auto element = *oldType.getElement(op.getFieldNameAttr());
3634 if (
auto structType = dyn_cast<hw::StructType>(newType)) {
3635 auto tagType = structType.getFieldType(
"tag");
3636 auto tagValue = IntegerAttr::get(tagType, element.value.getValue());
3637 auto tag = sv::LocalParamOp::create(builder, op.getLoc(), tagType, tagValue,
3639 auto bodyType = structType.getFieldType(
"body");
3640 auto body = hw::UnionCreateOp::create(builder, bodyType, tagName, input);
3641 SmallVector<Value> operands = {tag.getResult(), body.getResult()};
3642 return setLoweringTo<hw::StructCreateOp>(op, structType, operands);
3644 auto tagValue = IntegerAttr::get(newType, element.value.getValue());
3645 return setLoweringTo<sv::LocalParamOp>(op, newType, tagValue, tagName);
3648LogicalResult FIRRTLLowering::visitExpr(AggregateConstantOp op) {
3649 auto resultType =
lowerType(op.getResult().getType());
3651 getOrCreateAggregateConstantAttribute(op.getFieldsAttr(), resultType);
3653 return setLoweringTo<hw::AggregateConstantOp>(op, resultType,
3654 cast<ArrayAttr>(attr));
3657LogicalResult FIRRTLLowering::visitExpr(IsTagOp op) {
3659 auto tagName = op.getFieldNameAttr();
3660 auto lhs = getLoweredValue(op.getInput());
3661 if (isa<hw::StructType>(lhs.getType()))
3664 auto index = op.getFieldIndex();
3665 auto enumType = op.getInput().getType().base();
3666 auto tagValue = enumType.getElementValueAttr(index);
3667 auto tagValueType = IntegerType::get(op.getContext(), enumType.getTagWidth());
3668 auto loweredTagValue = IntegerAttr::get(tagValueType, tagValue.getValue());
3669 auto rhs = sv::LocalParamOp::create(builder, op.getLoc(), tagValueType,
3670 loweredTagValue, tagName);
3672 Type resultType = builder.getIntegerType(1);
3673 return setLoweringTo<comb::ICmpOp>(op, resultType, ICmpPredicate::eq, lhs,
3677LogicalResult FIRRTLLowering::visitExpr(SubtagOp op) {
3680 return setLowering(op, Value());
3682 auto tagName = op.getFieldNameAttr();
3683 auto input = getLoweredValue(op.getInput());
3685 return setLoweringTo<hw::UnionExtractOp>(op, field, tagName);
3688LogicalResult FIRRTLLowering::visitExpr(TagExtractOp op) {
3691 return setLowering(op, Value());
3693 auto input = getLoweredValue(op.getInput());
3699 if (isa<hw::StructType>(input.getType())) {
3700 return setLoweringTo<hw::StructExtractOp>(op, input,
"tag");
3705 return setLowering(op, input);
3712LogicalResult FIRRTLLowering::visitDecl(WireOp op) {
3713 auto origResultType = op.getResult().getType();
3717 if (!type_isa<FIRRTLType>(origResultType)) {
3718 createBackedge(op.getResult(), origResultType);
3722 auto resultType =
lowerType(origResultType);
3726 if (resultType.isInteger(0)) {
3727 if (op.getInnerSym())
3728 return op.emitError(
"zero width wire is referenced by name [")
3729 << *op.getInnerSym() <<
"] (e.g. in an XMR) but must be removed";
3730 return setLowering(op.getResult(), Value());
3734 auto innerSym = lowerInnerSymbol(op);
3735 auto name = op.getNameAttr();
3738 auto wire = hw::WireOp::create(
3739 builder, op.getLoc(), getOrCreateZConstant(resultType), name, innerSym);
3741 if (
auto svAttrs = sv::getSVAttributes(op))
3742 sv::setSVAttributes(wire, svAttrs);
3744 return setLowering(op.getResult(), wire);
3747LogicalResult FIRRTLLowering::visitDecl(VerbatimWireOp op) {
3748 auto resultTy =
lowerType(op.getType());
3751 resultTy = sv::InOutType::get(op.getContext(), resultTy);
3753 SmallVector<Value, 4> operands;
3754 operands.reserve(op.getSubstitutions().size());
3755 for (
auto operand : op.getSubstitutions()) {
3756 auto lowered = getLoweredValue(operand);
3759 operands.push_back(lowered);
3762 ArrayAttr symbols = op.getSymbolsAttr();
3764 symbols = ArrayAttr::get(op.getContext(), {});
3766 return setLoweringTo<sv::VerbatimExprSEOp>(op, resultTy, op.getTextAttr(),
3770LogicalResult FIRRTLLowering::visitDecl(NodeOp op) {
3771 auto operand = getLoweredValue(op.getInput());
3773 return handleZeroBit(op.getInput(), [&]() -> LogicalResult {
3774 if (op.getInnerSym())
3775 return op.emitError(
"zero width node is referenced by name [")
3776 << *op.getInnerSym()
3777 <<
"] (e.g. in an XMR) but must be "
3779 return setLowering(op.getResult(), Value());
3785 auto name = op.getNameAttr();
3786 auto innerSym = lowerInnerSymbol(op);
3789 operand = hw::WireOp::create(builder, operand, name, innerSym);
3792 if (
auto svAttrs = sv::getSVAttributes(op)) {
3794 operand = hw::WireOp::create(builder, operand, name);
3795 sv::setSVAttributes(operand.getDefiningOp(), svAttrs);
3798 return setLowering(op.getResult(), operand);
3801LogicalResult FIRRTLLowering::visitDecl(RegOp op) {
3802 auto resultType =
lowerType(op.getResult().getType());
3805 if (resultType.isInteger(0))
3806 return setLowering(op.getResult(), Value());
3808 Value clockVal = getLoweredValue(op.getClockVal());
3813 auto innerSym = lowerInnerSymbol(op);
3814 Backedge inputEdge = backedgeBuilder.
get(resultType);
3815 auto reg = seq::FirRegOp::create(builder, inputEdge, clockVal,
3816 op.getNameAttr(), innerSym);
3819 if (
auto randomRegister = op->getAttr(
"firrtl.random_init_register"))
3820 reg->setAttr(
"firrtl.random_init_register", randomRegister);
3821 if (
auto randomStart = op->getAttr(
"firrtl.random_init_start"))
3822 reg->setAttr(
"firrtl.random_init_start", randomStart);
3823 if (
auto randomEnd = op->getAttr(
"firrtl.random_init_end"))
3824 reg->setAttr(
"firrtl.random_init_end", randomEnd);
3827 if (
auto svAttrs = sv::getSVAttributes(op))
3828 sv::setSVAttributes(reg, svAttrs);
3831 (void)setLowering(op.getResult(),
reg);
3835LogicalResult FIRRTLLowering::visitDecl(RegResetOp op) {
3836 auto resultType =
lowerType(op.getResult().getType());
3839 if (resultType.isInteger(0))
3840 return setLowering(op.getResult(), Value());
3842 Value clockVal = getLoweredValue(op.getClockVal());
3843 Value resetSignal = getLoweredValue(op.getResetSignal());
3845 Value resetValue = getLoweredAndExtOrTruncValue(
3846 op.getResetValue(), type_cast<FIRRTLBaseType>(op.getResult().getType()));
3848 if (!clockVal || !resetSignal || !resetValue)
3852 auto innerSym = lowerInnerSymbol(op);
3853 bool isAsync = type_isa<AsyncResetType>(op.getResetSignal().getType());
3854 Backedge inputEdge = backedgeBuilder.
get(resultType);
3856 seq::FirRegOp::create(builder, inputEdge, clockVal, op.getNameAttr(),
3857 resetSignal, resetValue, innerSym, isAsync);
3860 if (
auto randomRegister = op->getAttr(
"firrtl.random_init_register"))
3861 reg->setAttr(
"firrtl.random_init_register", randomRegister);
3862 if (
auto randomStart = op->getAttr(
"firrtl.random_init_start"))
3863 reg->setAttr(
"firrtl.random_init_start", randomStart);
3864 if (
auto randomEnd = op->getAttr(
"firrtl.random_init_end"))
3865 reg->setAttr(
"firrtl.random_init_end", randomEnd);
3868 if (
auto svAttrs = sv::getSVAttributes(op))
3869 sv::setSVAttributes(reg, svAttrs);
3872 (void)setLowering(op.getResult(),
reg);
3877LogicalResult FIRRTLLowering::visitDecl(MemOp op) {
3880 if (type_isa<BundleType>(op.getDataType()))
3881 return op.emitOpError(
3882 "should have already been lowered from a ground type to an aggregate "
3883 "type using the LowerTypes pass. Use "
3884 "'firtool --lower-types' or 'circt-opt "
3885 "--pass-pipeline='firrtl.circuit(firrtl-lower-types)' "
3891 auto memType = seq::FirMemType::get(
3894 : std::optional<uint32_t>());
3896 seq::FirMemInitAttr memInit;
3897 if (
auto init = op.getInitAttr())
3898 memInit = seq::FirMemInitAttr::get(init.getContext(), init.getFilename(),
3899 init.getIsBinary(), init.getIsInline());
3901 auto memDecl = seq::FirMemOp::create(
3904 op.getInnerSymAttr(), memInit, op.getPrefixAttr(), Attribute{});
3907 if (
auto file = parent->getAttrOfType<hw::OutputFileAttr>(
"output_file")) {
3909 if (!file.isDirectory())
3910 dir = hw::OutputFileAttr::getAsDirectory(builder.getContext(),
3911 file.getDirectory());
3912 memDecl.setOutputFileAttr(dir);
3918 for (
size_t i = 0, e = op.getNumResults(); i != e; ++i) {
3920 auto addOutput = [&](StringRef field,
size_t width, Value value) {
3923 (void)setLowering(a, value);
3929 auto addInput = [&](StringRef field, Value backedge) {
3931 if (cast<FIRRTLBaseType>(
a.getType())
3933 .getBitWidthOrSentinel() > 0)
3934 (void)setLowering(a, backedge);
3940 auto addInputPort = [&](StringRef field,
size_t width) -> Value {
3944 Value backedge, portValue;
3946 portValue = getOrCreateXConstant(1);
3948 auto portType = IntegerType::get(op.getContext(), width);
3949 backedge = portValue = createBackedge(builder.getLoc(), portType);
3951 addInput(field, backedge);
3955 auto addClock = [&](StringRef field) -> Value {
3956 Type clockTy = seq::ClockType::get(op.getContext());
3957 Value portValue = createBackedge(builder.getLoc(), clockTy);
3958 addInput(field, portValue);
3962 auto memportKind = op.getPortKind(i);
3963 if (memportKind == MemOp::PortKind::Read) {
3964 auto addr = addInputPort(
"addr", op.getAddrBits());
3965 auto en = addInputPort(
"en", 1);
3966 auto clk = addClock(
"clk");
3967 auto data = seq::FirMemReadOp::create(builder, memDecl,
addr,
clk,
en);
3969 }
else if (memportKind == MemOp::PortKind::ReadWrite) {
3970 auto addr = addInputPort(
"addr", op.getAddrBits());
3971 auto en = addInputPort(
"en", 1);
3972 auto clk = addClock(
"clk");
3975 auto mode = addInputPort(
"wmode", 1);
3977 mode = builder.createOrFold<
comb::AndOp>(mode, addInputPort(
"wmask", 1),
3984 auto rdata = seq::FirMemReadWriteOp::create(builder, memDecl,
addr,
clk,
3988 auto addr = addInputPort(
"addr", op.getAddrBits());
3991 auto en = addInputPort(
"en", 1);
3995 auto clk = addClock(
"clk");
4009FIRRTLLowering::prepareInstanceOperands(ArrayRef<PortInfo> portInfo,
4010 Operation *instanceOp,
4011 SmallVectorImpl<Value> &inputOperands) {
4013 for (
size_t portIndex = 0, e = portInfo.size(); portIndex != e; ++portIndex) {
4014 auto &port = portInfo[portIndex];
4017 instanceOp->emitOpError(
"could not lower type of port ") << port.name;
4022 if (portType.isInteger(0))
4026 if (port.isOutput())
4029 auto portResult = instanceOp->getResult(portIndex);
4030 assert(portResult &&
"invalid IR, couldn't find port");
4034 if (port.isInput()) {
4035 inputOperands.push_back(createBackedge(portResult, portType));
4041 if (type_isa<AnalogType>(portResult.getType()) && portResult.hasOneUse()) {
4042 if (
auto attach = dyn_cast<AttachOp>(*portResult.getUsers().begin())) {
4044 auto loweredResult = getPossiblyInoutLoweredValue(source);
4045 inputOperands.push_back(loweredResult);
4046 (void)setLowering(portResult, loweredResult);
4055 "." + port.getName().str() +
".wire");
4059 (void)setLowering(portResult, wire);
4060 inputOperands.push_back(wire);
4066LogicalResult FIRRTLLowering::visitDecl(InstanceOp oldInstance) {
4067 Operation *oldModule =
4068 oldInstance.getReferencedModule(circuitState.getInstanceGraph());
4070 auto *newModule = circuitState.getNewModule(oldModule);
4072 oldInstance->emitOpError(
"could not find module [")
4073 << oldInstance.getModuleName() <<
"] referenced by instance";
4079 ArrayAttr parameters;
4080 if (
auto oldExtModule = dyn_cast<FExtModuleOp>(oldModule))
4085 SmallVector<PortInfo, 8> portInfo = cast<FModuleLike>(oldModule).getPorts();
4089 SmallVector<Value, 8> operands;
4090 if (failed(prepareInstanceOperands(portInfo, oldInstance, operands)))
4097 auto innerSym = oldInstance.getInnerSymAttr();
4098 if (oldInstance.getLowerToBind()) {
4101 oldInstance.getContext(), oldInstance.getInnerSymAttr(), 0,
4104 auto bindOp = sv::BindOp::create(builder, theModule.getNameAttr(),
4105 innerSym.getSymName());
4108 if (
auto outputFile = oldInstance->getAttr(
"output_file"))
4109 bindOp->setAttr(
"output_file", outputFile);
4112 circuitState.addBind(bindOp);
4117 hw::InstanceOp::create(builder, newModule, oldInstance.getNameAttr(),
4118 operands, parameters, innerSym);
4120 if (oldInstance.getLowerToBind() || oldInstance.getDoNotPrint())
4121 newInstance.setDoNotPrintAttr(builder.getUnitAttr());
4123 if (newInstance.getInnerSymAttr())
4124 if (
auto forceName = circuitState.instanceForceNames.lookup(
4125 {newInstance->getParentOfType<hw::HWModuleOp>().getNameAttr(),
4126 newInstance.getInnerNameAttr()}))
4127 newInstance->setAttr(
"hw.verilogName", forceName);
4131 unsigned resultNo = 0;
4132 for (
size_t portIndex = 0, e = portInfo.size(); portIndex != e; ++portIndex) {
4133 auto &port = portInfo[portIndex];
4137 Value resultVal = newInstance.getResult(resultNo);
4139 auto oldPortResult = oldInstance.getResult(portIndex);
4140 (void)setLowering(oldPortResult, resultVal);
4146LogicalResult FIRRTLLowering::visitDecl(InstanceChoiceOp oldInstanceChoice) {
4147 if (oldInstanceChoice.getInnerSymAttr()) {
4148 oldInstanceChoice->emitOpError(
4149 "instance choice with inner sym cannot be lowered");
4154 FlatSymbolRefAttr instanceMacro = oldInstanceChoice.getInstanceMacroAttr();
4156 return oldInstanceChoice->emitOpError(
4157 "must have instance_macro attribute set before "
4161 auto moduleNames = oldInstanceChoice.getModuleNamesAttr();
4162 auto caseNames = oldInstanceChoice.getCaseNamesAttr();
4165 auto defaultModuleName = oldInstanceChoice.getDefaultTargetAttr();
4166 auto *defaultModuleNode =
4167 circuitState.getInstanceGraph().lookup(defaultModuleName.getAttr());
4169 Operation *defaultModule = defaultModuleNode->getModule();
4173 SmallVector<PortInfo, 8> portInfo =
4174 cast<FModuleLike>(defaultModule).getPorts();
4177 SmallVector<Value, 8> inputOperands;
4179 prepareInstanceOperands(portInfo, oldInstanceChoice, inputOperands)))
4183 SmallVector<sv::WireOp, 8> outputWires;
4184 StringRef wirePrefix = oldInstanceChoice.getInstanceName();
4185 for (
size_t portIndex = 0, e = portInfo.size(); portIndex != e; ++portIndex) {
4186 auto &port = portInfo[portIndex];
4190 if (!portType || portType.isInteger(0))
4193 builder, portType, wirePrefix.str() +
"." + port.getName().str());
4194 outputWires.push_back(wire);
4195 if (failed(setLowering(oldInstanceChoice.getResult(portIndex), wire)))
4199 auto optionName = oldInstanceChoice.getOptionNameAttr();
4202 auto createInstanceAndAssign = [&](Operation *oldMod,
4203 StringRef suffix) -> hw::InstanceOp {
4204 auto *newMod = circuitState.getNewModule(oldMod);
4206 ArrayAttr parameters;
4207 if (
auto oldExtModule = dyn_cast<FExtModuleOp>(oldMod))
4211 SmallString<64> instName;
4212 instName = oldInstanceChoice.getInstanceName();
4213 if (!suffix.empty()) {
4219 hw::InstanceOp::create(builder, newMod, builder.getStringAttr(instName),
4220 inputOperands, parameters,
nullptr);
4226 for (
unsigned i = 0; i < inst.getNumResults(); ++i)
4233 SmallVector<StringAttr> macroNames;
4234 SmallVector<Operation *> altModules;
4235 for (
size_t i = 0, e = caseNames.size(); i < e; ++i) {
4236 altModules.push_back(
4237 circuitState.getInstanceGraph()
4238 .lookup(cast<FlatSymbolRefAttr>(moduleNames[i + 1]).getAttr())
4242 auto optionCaseMacroRef = circuitState.macroTable.getMacro(
4243 optionName, cast<SymbolRefAttr>(caseNames[i]).getLeafReference());
4244 if (!optionCaseMacroRef)
4245 return oldInstanceChoice->emitOpError(
4246 "failed to get macro for option case");
4247 macroNames.push_back(optionCaseMacroRef.getAttr());
4251 sv::createNestedIfDefs(
4254 [&](StringRef macro, std::function<
void()> thenCtor,
4255 std::function<
void()> elseCtor) {
4256 addToIfDefBlock(macro, std::move(thenCtor), std::move(elseCtor));
4260 for (
size_t i = index + 1; i < macroNames.size(); ++i) {
4261 sv::IfDefOp::create(
4262 builder, oldInstanceChoice.getLoc(), macroNames[i],
4264 SmallString<256> errorMessage;
4265 llvm::raw_svector_ostream os(errorMessage);
4266 os <<
"Multiple instance choice options defined for option '"
4267 << optionName.getValue() <<
"': '"
4268 << macroNames[index].getValue() <<
"' and '"
4269 << macroNames[i].getValue() <<
"'";
4270 sv::ErrorOp::create(builder, oldInstanceChoice.getLoc(),
4271 builder.getStringAttr(errorMessage));
4277 cast<SymbolRefAttr>(caseNames[index]).getLeafReference();
4279 createInstanceAndAssign(altModules[index], caseSymRef.getValue());
4281 sv::MacroDefOp::create(builder, inst.getLoc(), instanceMacro,
4282 builder.getStringAttr(
"{{0}}"),
4283 builder.getArrayAttr({hw::InnerRefAttr::get(
4284 theModule.getNameAttr(),
4285 inst.getInnerSymAttr().getSymName())}));
4289 SmallString<256> errorMessage;
4290 llvm::raw_svector_ostream os(errorMessage);
4291 os <<
"Required instance choice option '" << optionName.getValue()
4292 <<
"' not selected, must define one of: ";
4293 llvm::interleaveComma(macroNames, os, [&](StringAttr macro) {
4294 os <<
"'" << macro.getValue() <<
"'";
4296 sv::ErrorOp::create(builder, oldInstanceChoice.getLoc(),
4297 builder.getStringAttr(errorMessage));
4303LogicalResult FIRRTLLowering::visitDecl(ContractOp oldOp) {
4304 SmallVector<Value> inputs;
4305 SmallVector<Type> types;
4306 for (
auto input : oldOp.getInputs()) {
4307 auto lowered = getLoweredValue(input);
4310 inputs.push_back(lowered);
4311 types.push_back(lowered.getType());
4314 auto newOp = verif::ContractOp::create(builder, types, inputs);
4315 newOp->setDiscardableAttrs(oldOp->getDiscardableAttrDictionary());
4316 auto &body = newOp.getBody().emplaceBlock();
4318 for (
auto [newResult, oldResult, oldArg] :
4319 llvm::zip(newOp.getResults(), oldOp.getResults(),
4320 oldOp.getBody().getArguments())) {
4321 if (failed(setLowering(oldResult, newResult)))
4323 if (failed(setLowering(oldArg, newResult)))
4327 body.getOperations().splice(body.end(),
4328 oldOp.getBody().front().getOperations());
4329 addToWorklist(body);
4339LogicalResult FIRRTLLowering::lowerNoopCast(Operation *op) {
4340 auto operand = getPossiblyInoutLoweredValue(op->getOperand(0));
4345 return setLowering(op->getResult(0), operand);
4348LogicalResult FIRRTLLowering::visitExpr(AsSIntPrimOp op) {
4349 if (isa<ClockType>(op.getInput().getType()))
4350 return setLowering(op->getResult(0),
4351 getLoweredNonClockValue(op.getInput()));
4352 return lowerNoopCast(op);
4355LogicalResult FIRRTLLowering::visitExpr(AsUIntPrimOp op) {
4356 if (isa<ClockType>(op.getInput().getType()))
4357 return setLowering(op->getResult(0),
4358 getLoweredNonClockValue(op.getInput()));
4359 return lowerNoopCast(op);
4362LogicalResult FIRRTLLowering::visitExpr(AsClockPrimOp op) {
4363 return setLoweringTo<seq::ToClockOp>(op, getLoweredValue(op.getInput()));
4366LogicalResult FIRRTLLowering::visitUnrealizedConversionCast(
4367 mlir::UnrealizedConversionCastOp op) {
4369 if (op.getNumOperands() != 1 || op.getNumResults() != 1)
4372 auto operand = op.getOperand(0);
4373 auto result = op.getResult(0);
4376 if (type_isa<FIRRTLType>(operand.getType()) &&
4377 type_isa<FIRRTLType>(result.getType()))
4378 return lowerNoopCast(op);
4382 if (!type_isa<FIRRTLType>(operand.getType())) {
4383 if (type_isa<FIRRTLType>(result.getType()))
4384 return setLowering(result, getPossiblyInoutLoweredValue(operand));
4390 auto loweredResult = getLoweredValue(operand);
4391 if (!loweredResult) {
4394 if (operand.getType().isSignlessInteger(0)) {
4395 return setLowering(result, Value());
4402 result.replaceAllUsesWith(loweredResult);
4406LogicalResult FIRRTLLowering::visitExpr(HWStructCastOp op) {
4409 if (
auto opStructType = dyn_cast<hw::StructType>(op.getOperand().getType()))
4410 return setLowering(op, op.getOperand());
4414 auto result = getLoweredValue(op.getOperand());
4420 op.replaceAllUsesWith(result);
4424LogicalResult FIRRTLLowering::visitExpr(BitCastOp op) {
4425 auto operand = getLoweredValue(op.getOperand());
4428 auto resultType =
lowerType(op.getType());
4432 return setLoweringTo<hw::BitcastOp>(op, resultType, operand);
4435LogicalResult FIRRTLLowering::visitExpr(CvtPrimOp op) {
4436 auto operand = getLoweredValue(op.getOperand());
4440 if (type_cast<IntType>(op.getOperand().getType()).isUnsigned())
4441 return setLowering(op, getOrCreateIntConstant(1, 0));
4443 return setLowering(op, Value());
4448 if (type_cast<IntType>(op.getOperand().getType()).isSigned())
4449 return setLowering(op, operand);
4452 auto zero = getOrCreateIntConstant(1, 0);
4453 return setLoweringTo<comb::ConcatOp>(op, zero, operand);
4456LogicalResult FIRRTLLowering::visitExpr(NotPrimOp op) {
4457 auto operand = getLoweredValue(op.getInput());
4461 auto allOnes = getOrCreateIntConstant(
4462 APInt::getAllOnes(operand.getType().getIntOrFloatBitWidth()));
4463 return setLoweringTo<comb::XorOp>(op, operand, allOnes,
true);
4466LogicalResult FIRRTLLowering::visitExpr(NegPrimOp op) {
4469 auto operand = getLoweredAndExtendedValue(op.getInput(), op.getType());
4473 auto resultType =
lowerType(op.getType());
4475 auto zero = getOrCreateIntConstant(resultType.getIntOrFloatBitWidth(), 0);
4476 return setLoweringTo<comb::SubOp>(op, zero, operand,
true);
4480LogicalResult FIRRTLLowering::visitExpr(PadPrimOp op) {
4481 auto operand = getLoweredAndExtendedValue(op.getInput(), op.getType());
4484 return setLowering(op, operand);
4487LogicalResult FIRRTLLowering::visitExpr(XorRPrimOp op) {
4488 auto operand = getLoweredValue(op.getInput());
4491 return setLowering(op, getOrCreateIntConstant(1, 0));
4496 return setLoweringTo<comb::ParityOp>(op, builder.getIntegerType(1), operand,
4500LogicalResult FIRRTLLowering::visitExpr(AndRPrimOp op) {
4501 auto operand = getLoweredValue(op.getInput());
4504 return setLowering(op, getOrCreateIntConstant(1, 1));
4509 return setLoweringTo<comb::ICmpOp>(
4510 op, ICmpPredicate::eq, operand,
4511 getOrCreateIntConstant(
4512 APInt::getAllOnes(operand.getType().getIntOrFloatBitWidth())),
4516LogicalResult FIRRTLLowering::visitExpr(OrRPrimOp op) {
4517 auto operand = getLoweredValue(op.getInput());
4520 return setLowering(op, getOrCreateIntConstant(1, 0));
4526 return setLoweringTo<comb::ICmpOp>(
4527 op, ICmpPredicate::ne, operand,
4528 getOrCreateIntConstant(operand.getType().getIntOrFloatBitWidth(), 0),
4536template <
typename ResultOpType>
4537LogicalResult FIRRTLLowering::lowerBinOpToVariadic(Operation *op) {
4538 auto resultType = op->getResult(0).getType();
4539 auto lhs = getLoweredAndExtendedValue(op->getOperand(0), resultType);
4540 auto rhs = getLoweredAndExtendedValue(op->getOperand(1), resultType);
4544 return setLoweringTo<ResultOpType>(op, lhs, rhs,
true);
4550template <
typename ResultOpType>
4551LogicalResult FIRRTLLowering::lowerElementwiseLogicalOp(Operation *op) {
4552 auto resultType = op->getResult(0).getType();
4553 auto lhs = getLoweredAndExtendedValue(op->getOperand(0), resultType);
4554 auto rhs = getLoweredAndExtendedValue(op->getOperand(1), resultType);
4565 auto intType = builder.getIntegerType(*bitwidth);
4566 auto retType = lhs.getType();
4569 auto result = builder.createOrFold<ResultOpType>(lhs, rhs,
true);
4570 return setLoweringTo<hw::BitcastOp>(op, retType, result);
4575template <
typename ResultUn
signedOpType,
typename ResultSignedOpType>
4576LogicalResult FIRRTLLowering::lowerBinOp(Operation *op) {
4578 auto resultType = op->getResult(0).getType();
4579 auto lhs = getLoweredAndExtendedValue(op->getOperand(0), resultType);
4580 auto rhs = getLoweredAndExtendedValue(op->getOperand(1), resultType);
4585 if (type_cast<IntType>(resultType).isSigned())
4586 return setLoweringTo<ResultSignedOpType>(op, lhs, rhs,
true);
4587 return setLoweringTo<ResultUnsignedOpType>(op, lhs, rhs,
true);
4592LogicalResult FIRRTLLowering::lowerCmpOp(Operation *op, ICmpPredicate signedOp,
4593 ICmpPredicate unsignedOp) {
4595 auto lhsIntType = type_cast<IntType>(op->getOperand(0).getType());
4596 auto rhsIntType = type_cast<IntType>(op->getOperand(1).getType());
4597 if (!lhsIntType.hasWidth() || !rhsIntType.hasWidth())
4601 if (cmpType.getWidth() == 0)
4602 cmpType = UIntType::get(builder.getContext(), 1);
4603 auto lhs = getLoweredAndExtendedValue(op->getOperand(0), cmpType);
4604 auto rhs = getLoweredAndExtendedValue(op->getOperand(1), cmpType);
4609 Type resultType = builder.getIntegerType(1);
4610 return setLoweringTo<comb::ICmpOp>(
4611 op, resultType, lhsIntType.isSigned() ? signedOp : unsignedOp, lhs, rhs,
4617template <
typename SignedOp,
typename Un
signedOp>
4618LogicalResult FIRRTLLowering::lowerDivLikeOp(Operation *op) {
4622 auto opType = type_cast<IntType>(op->getResult(0).getType());
4623 if (opType.getWidth() == 0)
4624 return setLowering(op->getResult(0), Value());
4628 auto lhs = getLoweredAndExtendedValue(op->getOperand(0), resultType);
4629 auto rhs = getLoweredAndExtendedValue(op->getOperand(1), resultType);
4634 if (opType.isSigned())
4635 result = builder.createOrFold<SignedOp>(lhs, rhs,
true);
4637 result = builder.createOrFold<UnsignedOp>(lhs, rhs,
true);
4639 if (
auto *definingOp = result.getDefiningOp())
4642 if (resultType == opType)
4643 return setLowering(op->getResult(0), result);
4644 return setLoweringTo<comb::ExtractOp>(op,
lowerType(opType), result, 0);
4647LogicalResult FIRRTLLowering::visitExpr(CatPrimOp op) {
4649 if (op.getInputs().empty())
4650 return setLowering(op, Value());
4652 SmallVector<Value> loweredOperands;
4655 for (
auto operand : op.getInputs()) {
4656 auto loweredOperand = getLoweredValue(operand);
4657 if (loweredOperand) {
4658 loweredOperands.push_back(loweredOperand);
4661 auto result =
handleZeroBit(operand, [&]() {
return success(); });
4669 if (loweredOperands.empty())
4670 return setLowering(op, Value());
4673 return setLoweringTo<comb::ConcatOp>(op, loweredOperands);
4680LogicalResult FIRRTLLowering::visitExpr(IsXIntrinsicOp op) {
4681 auto input = getLoweredNonClockValue(op.getArg());
4685 if (!isa<IntType>(input.getType())) {
4686 auto srcType = op.getArg().getType();
4688 assert(bitwidth &&
"Unknown width");
4689 auto intType = builder.getIntegerType(*bitwidth);
4690 input = builder.createOrFold<
hw::BitcastOp>(intType, input);
4693 return setLoweringTo<comb::ICmpOp>(
4694 op, ICmpPredicate::ceq, input,
4695 getOrCreateXConstant(input.getType().getIntOrFloatBitWidth()),
true);
4698LogicalResult FIRRTLLowering::visitStmt(FPGAProbeIntrinsicOp op) {
4699 auto operand = getLoweredValue(op.getInput());
4700 hw::WireOp::create(builder, operand);
4704LogicalResult FIRRTLLowering::visitExpr(PlusArgsTestIntrinsicOp op) {
4705 return setLoweringTo<sim::PlusArgsTestOp>(op, builder.getIntegerType(1),
4706 op.getFormatStringAttr());
4709LogicalResult FIRRTLLowering::visitExpr(PlusArgsValueIntrinsicOp op) {
4710 auto type =
lowerType(op.getResult().getType());
4714 auto valueOp = sim::PlusArgsValueOp::create(
4715 builder, builder.getIntegerType(1), type, op.getFormatStringAttr());
4716 if (failed(setLowering(op.getResult(), valueOp.getResult())))
4718 if (failed(setLowering(op.getFound(), valueOp.getFound())))
4723LogicalResult FIRRTLLowering::visitExpr(SizeOfIntrinsicOp op) {
4724 op.emitError(
"SizeOf should have been resolved.");
4728LogicalResult FIRRTLLowering::visitExpr(ClockGateIntrinsicOp op) {
4730 if (op.getTestEnable())
4731 testEnable = getLoweredValue(op.getTestEnable());
4732 return setLoweringTo<seq::ClockGateOp>(
4733 op, getLoweredValue(op.getInput()), getLoweredValue(op.getEnable()),
4734 testEnable, hw::InnerSymAttr{});
4737LogicalResult FIRRTLLowering::visitExpr(ClockInverterIntrinsicOp op) {
4738 auto operand = getLoweredValue(op.getInput());
4739 return setLoweringTo<seq::ClockInverterOp>(op, operand);
4742LogicalResult FIRRTLLowering::visitExpr(ClockDividerIntrinsicOp op) {
4743 auto operand = getLoweredValue(op.getInput());
4744 return setLoweringTo<seq::ClockDividerOp>(op, operand, op.getPow2());
4747LogicalResult FIRRTLLowering::visitExpr(LTLAndIntrinsicOp op) {
4748 return setLoweringToLTL<ltl::AndOp>(
4750 ValueRange{getLoweredValue(op.getLhs()), getLoweredValue(op.getRhs())});
4753LogicalResult FIRRTLLowering::visitExpr(LTLOrIntrinsicOp op) {
4754 return setLoweringToLTL<ltl::OrOp>(
4756 ValueRange{getLoweredValue(op.getLhs()), getLoweredValue(op.getRhs())});
4759LogicalResult FIRRTLLowering::visitExpr(LTLIntersectIntrinsicOp op) {
4760 return setLoweringToLTL<ltl::IntersectOp>(
4762 ValueRange{getLoweredValue(op.getLhs()), getLoweredValue(op.getRhs())});
4765LogicalResult FIRRTLLowering::visitExpr(LTLDelayIntrinsicOp op) {
4766 return setLoweringToLTL<ltl::DelayOp>(op, getLoweredValue(op.getInput()),
4767 op.getDelayAttr(), op.getLengthAttr());
4770LogicalResult FIRRTLLowering::visitExpr(LTLConcatIntrinsicOp op) {
4771 return setLoweringToLTL<ltl::ConcatOp>(
4773 ValueRange{getLoweredValue(op.getLhs()), getLoweredValue(op.getRhs())});
4776LogicalResult FIRRTLLowering::visitExpr(LTLRepeatIntrinsicOp op) {
4777 return setLoweringToLTL<ltl::RepeatOp>(op, getLoweredValue(op.getInput()),
4778 op.getBaseAttr(), op.getMoreAttr());
4781LogicalResult FIRRTLLowering::visitExpr(LTLGoToRepeatIntrinsicOp op) {
4782 return setLoweringToLTL<ltl::GoToRepeatOp>(
4783 op, getLoweredValue(op.getInput()), op.getBaseAttr(), op.getMoreAttr());
4786LogicalResult FIRRTLLowering::visitExpr(LTLNonConsecutiveRepeatIntrinsicOp op) {
4787 return setLoweringToLTL<ltl::NonConsecutiveRepeatOp>(
4788 op, getLoweredValue(op.getInput()), op.getBaseAttr(), op.getMoreAttr());
4791LogicalResult FIRRTLLowering::visitExpr(LTLNotIntrinsicOp op) {
4792 return setLoweringToLTL<ltl::NotOp>(op, getLoweredValue(op.getInput()));
4795LogicalResult FIRRTLLowering::visitExpr(LTLImplicationIntrinsicOp op) {
4796 return setLoweringToLTL<ltl::ImplicationOp>(
4798 ValueRange{getLoweredValue(op.getLhs()), getLoweredValue(op.getRhs())});
4801LogicalResult FIRRTLLowering::visitExpr(LTLUntilIntrinsicOp op) {
4802 return setLoweringToLTL<ltl::UntilOp>(
4804 ValueRange{getLoweredValue(op.getLhs()), getLoweredValue(op.getRhs())});
4807LogicalResult FIRRTLLowering::visitExpr(LTLEventuallyIntrinsicOp op) {
4808 return setLoweringToLTL<ltl::EventuallyOp>(op,
4809 getLoweredValue(op.getInput()));
4812LogicalResult FIRRTLLowering::visitExpr(LTLPastIntrinsicOp op) {
4815 clk = getLoweredNonClockValue(op.getClock());
4816 return setLoweringToLTL<ltl::PastOp>(op, getLoweredValue(op.getInput()),
4817 op.getDelayAttr(),
clk);
4820LogicalResult FIRRTLLowering::visitExpr(LTLClockIntrinsicOp op) {
4821 return setLoweringToLTL<ltl::ClockOp>(op, getLoweredValue(op.getInput()),
4822 ltl::ClockEdge::Pos,
4823 getLoweredNonClockValue(op.getClock()));
4826template <
typename TargetOp,
typename IntrinsicOp>
4827LogicalResult FIRRTLLowering::lowerVerifIntrinsicOp(IntrinsicOp op) {
4828 auto property = getLoweredValue(op.getProperty());
4829 auto enable = op.getEnable() ? getLoweredValue(op.getEnable()) : Value();
4830 TargetOp::create(builder, property, enable, op.getLabelAttr());
4834LogicalResult FIRRTLLowering::visitStmt(VerifAssertIntrinsicOp op) {
4835 return lowerVerifIntrinsicOp<verif::AssertOp>(op);
4838LogicalResult FIRRTLLowering::visitStmt(VerifAssumeIntrinsicOp op) {
4839 return lowerVerifIntrinsicOp<verif::AssumeOp>(op);
4842LogicalResult FIRRTLLowering::visitStmt(VerifCoverIntrinsicOp op) {
4843 return lowerVerifIntrinsicOp<verif::CoverOp>(op);
4846LogicalResult FIRRTLLowering::visitStmt(VerifRequireIntrinsicOp op) {
4847 if (!isa<verif::ContractOp>(op->getParentOp()))
4848 return lowerVerifIntrinsicOp<verif::AssertOp>(op);
4849 return lowerVerifIntrinsicOp<verif::RequireOp>(op);
4852LogicalResult FIRRTLLowering::visitStmt(VerifEnsureIntrinsicOp op) {
4853 if (!isa<verif::ContractOp>(op->getParentOp()))
4854 return lowerVerifIntrinsicOp<verif::AssertOp>(op);
4855 return lowerVerifIntrinsicOp<verif::EnsureOp>(op);
4858LogicalResult FIRRTLLowering::visitExpr(HasBeenResetIntrinsicOp op) {
4859 auto clock = getLoweredNonClockValue(op.getClock());
4860 auto reset = getLoweredValue(op.getReset());
4861 if (!clock || !reset)
4863 auto resetType = op.getReset().getType();
4864 auto uintResetType = dyn_cast<UIntType>(resetType);
4865 auto isSync = uintResetType && uintResetType.getWidth() == 1;
4866 auto isAsync = isa<AsyncResetType>(resetType);
4867 if (!isAsync && !isSync) {
4868 auto d = op.emitError(
"uninferred reset passed to 'has_been_reset'; "
4869 "requires sync or async reset");
4870 d.attachNote() <<
"reset is of type " << resetType
4871 <<
", should be '!firrtl.uint<1>' or '!firrtl.asyncreset'";
4874 return setLoweringTo<verif::HasBeenResetOp>(op, clock, reset, isAsync);
4881LogicalResult FIRRTLLowering::visitExpr(BitsPrimOp op) {
4882 auto input = getLoweredValue(op.getInput());
4886 Type resultType = builder.getIntegerType(op.getHi() - op.getLo() + 1);
4887 return setLoweringTo<comb::ExtractOp>(op, resultType, input, op.getLo());
4890LogicalResult FIRRTLLowering::visitExpr(InvalidValueOp op) {
4891 auto resultTy =
lowerType(op.getType());
4898 if (type_isa<AnalogType>(op.getType()))
4901 return setLoweringTo<sv::WireOp>(op, resultTy,
".invalid_analog");
4904 if (type_cast<FIRRTLBaseType>(op.getType()).containsAnalog())
4915 auto constant = getOrCreateIntConstant(*bitwidth, 0);
4917 if (!type_isa<IntegerType>(resultTy))
4919 return setLowering(op, constant);
4923 op.emitOpError(
"unsupported type");
4927LogicalResult FIRRTLLowering::visitExpr(HeadPrimOp op) {
4928 auto input = getLoweredValue(op.getInput());
4931 auto inWidth = type_cast<IntegerType>(input.getType()).getWidth();
4932 if (op.getAmount() == 0)
4933 return setLowering(op, Value());
4934 Type resultType = builder.getIntegerType(op.getAmount());
4935 return setLoweringTo<comb::ExtractOp>(op, resultType, input,
4936 inWidth - op.getAmount());
4939LogicalResult FIRRTLLowering::visitExpr(ShlPrimOp op) {
4940 auto input = getLoweredValue(op.getInput());
4943 if (op.getAmount() == 0)
4945 return setLowering(op, getOrCreateIntConstant(op.getAmount(), 0));
4950 if (op.getAmount() == 0)
4951 return setLowering(op, input);
4953 auto zero = getOrCreateIntConstant(op.getAmount(), 0);
4954 return setLoweringTo<comb::ConcatOp>(op, input, zero);
4957LogicalResult FIRRTLLowering::visitExpr(ShrPrimOp op) {
4958 auto input = getLoweredValue(op.getInput());
4963 auto inWidth = type_cast<IntegerType>(input.getType()).getWidth();
4964 auto shiftAmount = op.getAmount();
4965 if (shiftAmount >= inWidth) {
4967 if (type_cast<IntType>(op.getInput().getType()).isUnsigned())
4968 return setLowering(op, {});
4971 shiftAmount = inWidth - 1;
4974 Type resultType = builder.getIntegerType(inWidth - shiftAmount);
4975 return setLoweringTo<comb::ExtractOp>(op, resultType, input, shiftAmount);
4978LogicalResult FIRRTLLowering::visitExpr(TailPrimOp op) {
4979 auto input = getLoweredValue(op.getInput());
4983 auto inWidth = type_cast<IntegerType>(input.getType()).getWidth();
4984 if (inWidth == op.getAmount())
4985 return setLowering(op, Value());
4986 Type resultType = builder.getIntegerType(inWidth - op.getAmount());
4987 return setLoweringTo<comb::ExtractOp>(op, resultType, input, 0);
4990LogicalResult FIRRTLLowering::visitExpr(MuxPrimOp op) {
4991 auto cond = getLoweredValue(op.getSel());
4992 auto ifTrue = getLoweredAndExtendedValue(op.getHigh(), op.getType());
4993 auto ifFalse = getLoweredAndExtendedValue(op.getLow(), op.getType());
4994 if (!cond || !ifTrue || !ifFalse)
4997 if (isa<ClockType>(op.getType()))
4998 return setLoweringTo<seq::ClockMuxOp>(op, cond, ifTrue, ifFalse);
4999 return setLoweringTo<comb::MuxOp>(op, ifTrue.getType(), cond, ifTrue, ifFalse,
5003LogicalResult FIRRTLLowering::visitExpr(Mux2CellIntrinsicOp op) {
5004 auto cond = getLoweredValue(op.getSel());
5005 auto ifTrue = getLoweredAndExtendedValue(op.getHigh(), op.getType());
5006 auto ifFalse = getLoweredAndExtendedValue(op.getLow(), op.getType());
5007 if (!cond || !ifTrue || !ifFalse)
5010 auto val = comb::MuxOp::create(builder, ifTrue.getType(), cond, ifTrue,
5012 return setLowering(op, createValueWithMuxAnnotation(val,
true));
5015LogicalResult FIRRTLLowering::visitExpr(Mux4CellIntrinsicOp op) {
5016 auto sel = getLoweredValue(op.getSel());
5017 auto v3 = getLoweredAndExtendedValue(op.getV3(), op.getType());
5018 auto v2 = getLoweredAndExtendedValue(op.getV2(), op.getType());
5019 auto v1 = getLoweredAndExtendedValue(op.getV1(), op.getType());
5020 auto v0 = getLoweredAndExtendedValue(op.getV0(), op.getType());
5021 if (!sel || !v3 || !v2 || !v1 || !v0)
5023 Value array[] = {v3, v2, v1, v0};
5026 return setLowering(op, createValueWithMuxAnnotation(val,
false));
5045Value FIRRTLLowering::createValueWithMuxAnnotation(Operation *op,
bool isMux2) {
5046 assert(op->getNumResults() == 1 &&
"only expect a single result");
5047 auto val = op->getResult(0);
5051 op, sv::SVAttributeAttr::get(builder.getContext(),
"cadence map_to_mux",
5058 OpBuilder::InsertionGuard guard(builder);
5059 builder.setInsertionPoint(op);
5060 StringRef namehint = isMux2 ?
"mux2cell_in" :
"mux4cell_in";
5061 for (
auto [idx, operand] :
llvm::enumerate(op->getOperands())) {
5063 op->getContext(),
nullptr, 0,
5066 hw::WireOp::create(builder, operand, namehint + Twine(idx), innerSym);
5067 op->setOperand(idx, wire);
5072 sv::setSVAttributes(assignOp,
5073 sv::SVAttributeAttr::get(builder.getContext(),
5074 "synopsys infer_mux_override",
5079Value FIRRTLLowering::createArrayIndexing(Value array, Value index) {
5081 auto size = hw::type_cast<hw::ArrayType>(array.getType()).getNumElements();
5086 if (!llvm::isPowerOf2_64(size)) {
5087 auto extElem = getOrCreateIntConstant(APInt(llvm::Log2_64_Ceil(size), 0));
5089 SmallVector<Value> temp(llvm::NextPowerOf2(size) - size, extValue);
5091 Value temp2[] = {ext.getResult(), array};
5097 return inBoundsRead;
5100LogicalResult FIRRTLLowering::visitExpr(MultibitMuxOp op) {
5102 auto index = getLoweredAndExtOrTruncValue(
5104 UIntType::get(op.getContext(),
5109 SmallVector<Value> loweredInputs;
5110 loweredInputs.reserve(op.getInputs().size());
5111 for (
auto input : op.getInputs()) {
5112 auto lowered = getLoweredAndExtendedValue(input, op.getType());
5115 loweredInputs.push_back(lowered);
5119 return setLowering(op, createArrayIndexing(array, index));
5122LogicalResult FIRRTLLowering::visitExpr(VerbatimExprOp op) {
5123 auto resultTy =
lowerType(op.getType());
5127 SmallVector<Value, 4> operands;
5128 operands.reserve(op.getSubstitutions().size());
5129 for (
auto operand : op.getSubstitutions()) {
5130 auto lowered = getLoweredValue(operand);
5133 operands.push_back(lowered);
5136 ArrayAttr symbols = op.getSymbolsAttr();
5138 symbols = ArrayAttr::get(op.getContext(), {});
5140 return setLoweringTo<sv::VerbatimExprOp>(op, resultTy, op.getTextAttr(),
5144LogicalResult FIRRTLLowering::visitExpr(XMRRefOp op) {
5148 Type baseType = op.getType().getType();
5151 if (isa<ClockType>(baseType))
5152 xmrType = builder.getIntegerType(1);
5156 return setLoweringTo<sv::XMRRefOp>(op, sv::InOutType::get(xmrType),
5157 op.getRef(), op.getVerbatimSuffixAttr());
5160LogicalResult FIRRTLLowering::visitExpr(XMRDerefOp op) {
5164 if (isa<ClockType>(op.getType()))
5165 xmrType = builder.getIntegerType(1);
5169 auto xmr = sv::XMRRefOp::create(builder, sv::InOutType::get(xmrType),
5170 op.getRef(), op.getVerbatimSuffixAttr());
5171 auto readXmr = getReadValue(xmr);
5172 if (!isa<ClockType>(op.getType()))
5173 return setLowering(op, readXmr);
5174 return setLoweringTo<seq::ToClockOp>(op, readXmr);
5179LogicalResult FIRRTLLowering::visitExpr(TimeOp op) {
return success(); }
5180LogicalResult FIRRTLLowering::visitExpr(HierarchicalModuleNameOp op) {
5188LogicalResult FIRRTLLowering::visitStmt(SkipOp op) {
5200FailureOr<bool> FIRRTLLowering::lowerConnect(Value destVal, Value srcVal) {
5201 auto srcType = srcVal.getType();
5202 auto dstType = destVal.getType();
5203 if (srcType != dstType &&
5204 (isa<hw::TypeAliasType>(srcType) || isa<hw::TypeAliasType>(dstType))) {
5207 return TypeSwitch<Operation *, FailureOr<bool>>(destVal.getDefiningOp())
5208 .Case<hw::WireOp>([&](
auto op) {
5209 maybeUnused(op.getInput());
5210 op.getInputMutable().assign(srcVal);
5213 .Case<seq::FirRegOp>([&](
auto op) {
5214 maybeUnused(op.getNext());
5215 op.getNextMutable().assign(srcVal);
5218 .Case<hw::StructExtractOp, hw::ArrayGetOp>([](
auto op) {
5221 op.emitOpError(
"used as connect destination");
5224 .Default([](
auto) {
return false; });
5227LogicalResult FIRRTLLowering::visitStmt(ConnectOp op) {
5228 auto dest = op.getDest();
5230 auto destType = type_cast<FIRRTLBaseType>(dest.getType()).getPassiveType();
5231 auto srcVal = getLoweredAndExtendedValue(op.getSrc(), destType);
5233 return handleZeroBit(op.getSrc(), []() { return success(); });
5235 auto destVal = getPossiblyInoutLoweredValue(dest);
5239 auto result = lowerConnect(destVal, srcVal);
5247 if (updateIfBackedge(destVal, srcVal))
5250 if (!isa<hw::InOutType>(destVal.getType()))
5251 return op.emitError(
"destination isn't an inout type");
5257LogicalResult FIRRTLLowering::visitStmt(MatchingConnectOp op) {
5258 auto dest = op.getDest();
5259 auto srcVal = getLoweredValue(op.getSrc());
5261 return handleZeroBit(op.getSrc(), []() { return success(); });
5263 auto destVal = getPossiblyInoutLoweredValue(dest);
5267 auto result = lowerConnect(destVal, srcVal);
5275 if (updateIfBackedge(destVal, srcVal))
5278 if (!isa<hw::InOutType>(destVal.getType()))
5279 return op.emitError(
"destination isn't an inout type");
5285LogicalResult FIRRTLLowering::visitStmt(ForceOp op) {
5286 if (circuitState.lowerToCore)
5287 return op.emitOpError(
"lower-to-core does not support firrtl.force");
5289 auto srcVal = getLoweredValue(op.getSrc());
5293 auto destVal = getPossiblyInoutLoweredValue(op.getDest());
5297 if (!isa<hw::InOutType>(destVal.getType()))
5298 return op.emitError(
"destination isn't an inout type");
5301 circuitState.addMacroDecl(builder.getStringAttr(
"SYNTHESIS"));
5302 addToIfDefBlock(
"SYNTHESIS", std::function<
void()>(), [&]() {
5303 addToInitialBlock([&]() { sv::ForceOp::create(builder, destVal, srcVal); });
5308LogicalResult FIRRTLLowering::visitStmt(RefForceOp op) {
5309 if (circuitState.lowerToCore)
5310 return op.emitOpError(
"lower-to-core does not support firrtl.ref.force");
5312 auto src = getLoweredNonClockValue(op.getSrc());
5313 auto clock = getLoweredNonClockValue(op.getClock());
5314 auto pred = getLoweredValue(op.getPredicate());
5315 if (!src || !clock || !pred)
5318 auto destVal = getPossiblyInoutLoweredValue(op.getDest());
5323 circuitState.addMacroDecl(builder.getStringAttr(
"SYNTHESIS"));
5324 addToIfDefBlock(
"SYNTHESIS", std::function<
void()>(), [&]() {
5325 addToAlwaysBlock(clock, [&]() {
5326 addIfProceduralBlock(
5327 pred, [&]() { sv::ForceOp::create(builder, destVal, src); });
5332LogicalResult FIRRTLLowering::visitStmt(RefForceInitialOp op) {
5333 if (circuitState.lowerToCore)
5334 return op.emitOpError(
5335 "lower-to-core does not support firrtl.ref.force_initial");
5337 auto src = getLoweredNonClockValue(op.getSrc());
5338 auto pred = getLoweredValue(op.getPredicate());
5342 auto destVal = getPossiblyInoutLoweredValue(op.getDest());
5347 circuitState.addMacroDecl(builder.getStringAttr(
"SYNTHESIS"));
5348 addToIfDefBlock(
"SYNTHESIS", std::function<
void()>(), [&]() {
5349 addToInitialBlock([&]() {
5350 addIfProceduralBlock(
5351 pred, [&]() { sv::ForceOp::create(builder, destVal, src); });
5356LogicalResult FIRRTLLowering::visitStmt(RefReleaseOp op) {
5357 if (circuitState.lowerToCore)
5358 return op.emitOpError(
"lower-to-core does not support firrtl.ref.release");
5360 auto clock = getLoweredNonClockValue(op.getClock());
5361 auto pred = getLoweredValue(op.getPredicate());
5362 if (!clock || !pred)
5365 auto destVal = getPossiblyInoutLoweredValue(op.getDest());
5370 circuitState.addMacroDecl(builder.getStringAttr(
"SYNTHESIS"));
5371 addToIfDefBlock(
"SYNTHESIS", std::function<
void()>(), [&]() {
5372 addToAlwaysBlock(clock, [&]() {
5373 addIfProceduralBlock(pred,
5374 [&]() { sv::ReleaseOp::create(builder, destVal); });
5379LogicalResult FIRRTLLowering::visitStmt(RefReleaseInitialOp op) {
5380 if (circuitState.lowerToCore)
5381 return op.emitOpError(
5382 "lower-to-core does not support firrtl.ref.release_initial");
5384 auto destVal = getPossiblyInoutLoweredValue(op.getDest());
5385 auto pred = getLoweredValue(op.getPredicate());
5386 if (!destVal || !pred)
5390 circuitState.addMacroDecl(builder.getStringAttr(
"SYNTHESIS"));
5391 addToIfDefBlock(
"SYNTHESIS", std::function<
void()>(), [&]() {
5392 addToInitialBlock([&]() {
5393 addIfProceduralBlock(pred,
5394 [&]() { sv::ReleaseOp::create(builder, destVal); });
5402 StringRef originalFormatString,
5403 ValueRange operands,
5404 StringAttr &result) {
5407 SmallString<32> formatString;
5408 for (
size_t i = 0, e = originalFormatString.size(), subIdx = 0; i != e; ++i) {
5409 char c = originalFormatString[i];
5413 formatString.push_back(c);
5416 SmallString<6> width;
5417 c = originalFormatString[++i];
5420 c = originalFormatString[++i];
5431 formatString.append(width);
5437 formatString.push_back(c);
5444 if (originalFormatString.slice(i, i + 4) !=
"{{}}") {
5445 formatString.push_back(c);
5449 auto substitution = operands[subIdx++];
5450 assert(type_isa<FStringType>(substitution.getType()) &&
5451 "the operand for a '{{}}' substitution must be an 'fstring' type");
5453 TypeSwitch<Operation *, LogicalResult>(substitution.getDefiningOp())
5454 .template Case<TimeOp>([&](
auto) {
5455 formatString.append(
"%0t");
5458 .
template Case<HierarchicalModuleNameOp>([&](
auto) {
5459 formatString.append(
"%m");
5462 .Default([&](
auto) {
5463 emitError(loc,
"has a substitution with an unimplemented "
5465 .attachNote(substitution.getLoc())
5466 <<
"op with an unimplemented lowering is here";
5476 formatString.push_back(c);
5480 result = StringAttr::get(loc->getContext(), formatString);
5487LogicalResult FIRRTLLowering::visitPrintfLike(
5488 T op,
const FileDescriptorInfo &fileDescriptorInfo,
bool usePrintfCond) {
5489 auto clock = getLoweredNonClockValue(op.getClock());
5490 auto cond = getLoweredValue(op.getCond());
5491 if (!clock || !cond)
5494 StringAttr formatString;
5496 op.getSubstitutions(), formatString)))
5499 auto fn = [&](Value fd) {
5500 SmallVector<Value> operands;
5501 if (failed(loweredFmtOperands(op.getSubstitutions(), operands)))
5503 sv::FWriteOp::create(builder, op.getLoc(), fd, formatString, operands);
5507 return lowerStatementWithFd(fileDescriptorInfo, clock, cond, fn,
5511LogicalResult FIRRTLLowering::visitStmt(PrintFOp op) {
5512 if (!circuitState.lowerToCore)
5513 return visitPrintfLike(op, {},
true);
5515 auto clock = getLoweredValue(op.getClock());
5516 auto cond = getLoweredValue(op.getCond());
5517 if (!clock || !cond)
5521 lowerSimFormatString(op.getFormatString(), op.getSubstitutions());
5522 if (failed(formatString))
5525 sim::PrintFormattedOp::create(builder, *formatString, clock, cond);
5529LogicalResult FIRRTLLowering::visitStmt(FPrintFOp op) {
5530 if (circuitState.lowerToCore)
5531 return op.emitOpError(
"lower-to-core does not support firrtl.fprintf yet");
5533 StringAttr outputFileAttr;
5535 op.getOutputFileSubstitutions(),
5539 FileDescriptorInfo outputFile(outputFileAttr,
5540 op.getOutputFileSubstitutions());
5541 return visitPrintfLike(op, outputFile,
false);
5545LogicalResult FIRRTLLowering::visitStmt(FFlushOp op) {
5546 if (circuitState.lowerToCore)
5547 return op.emitOpError(
"lower-to-core does not support firrtl.fflush yet");
5549 auto clock = getLoweredNonClockValue(op.getClock());
5550 auto cond = getLoweredValue(op.getCond());
5551 if (!clock || !cond)
5554 auto fn = [&](Value fd) {
5555 sv::FFlushOp::create(builder, op.getLoc(), fd);
5559 if (!op.getOutputFileAttr())
5560 return lowerStatementWithFd({}, clock, cond, fn,
false);
5564 StringAttr outputFileAttr;
5566 op.getOutputFileSubstitutions(),
5570 return lowerStatementWithFd(
5571 FileDescriptorInfo(outputFileAttr, op.getOutputFileSubstitutions()),
5572 clock, cond, fn,
false);
5577LogicalResult FIRRTLLowering::visitStmt(StopOp op) {
5578 auto clock = getLoweredValue(op.getClock());
5579 auto cond = getLoweredValue(op.getCond());
5580 if (!clock || !cond)
5583 circuitState.usedStopCond =
true;
5584 circuitState.addFragment(theModule,
"STOP_COND_FRAGMENT");
5587 sv::MacroRefExprOp::create(builder, cond.getType(),
"STOP_COND_");
5588 Value exitCond = builder.createOrFold<
comb::AndOp>(stopCond, cond,
true);
5590 sim::ClockedTerminateOp::create(builder, clock, exitCond,
5591 op.getExitCode() == 0,
5600template <
typename... Args>
5602 StringRef opName, Args &&...args) {
5603 if (opName ==
"assert")
5604 return sv::AssertOp::create(builder, std::forward<Args>(args)...);
5605 if (opName ==
"assume")
5606 return sv::AssumeOp::create(builder, std::forward<Args>(args)...);
5607 if (opName ==
"cover")
5608 return sv::CoverOp::create(builder, std::forward<Args>(args)...);
5609 llvm_unreachable(
"unknown verification op");
5615template <
typename... Args>
5617 StringRef opName, Args &&...args) {
5618 if (opName ==
"assert")
5619 return sv::AssertConcurrentOp::create(builder, std::forward<Args>(args)...);
5620 if (opName ==
"assume")
5621 return sv::AssumeConcurrentOp::create(builder, std::forward<Args>(args)...);
5622 if (opName ==
"cover")
5623 return sv::CoverConcurrentOp::create(builder, std::forward<Args>(args)...);
5624 llvm_unreachable(
"unknown verification op");
5628 switch (eventControl) {
5629 case EventControl::AtPosEdge:
5630 return verif::ClockEdge::Pos;
5631 case EventControl::AtEdge:
5632 return verif::ClockEdge::Both;
5633 case EventControl::AtNegEdge:
5634 return verif::ClockEdge::Neg;
5636 llvm_unreachable(
"unknown FIRRTL event control");
5639LogicalResult FIRRTLLowering::lowerVerificationStatementToCore(
5640 Operation *op, StringRef labelPrefix, Value opClock, Value opPredicate,
5641 Value opEnable, StringAttr opNameAttr, EventControl opEventControl) {
5642 auto guardsAttr = op->getAttrOfType<ArrayAttr>(
"guards");
5643 if (guardsAttr && !guardsAttr.empty())
5644 return op->emitOpError(
5645 "lower-to-core does not support guarded verification statements");
5647 auto clock = getLoweredNonClockValue(opClock);
5648 auto enable = getLoweredValue(opEnable);
5649 auto predicate = getLoweredValue(opPredicate);
5650 if (!clock || !enable || !predicate)
5654 if (opNameAttr && !opNameAttr.getValue().empty())
5655 label = StringAttr::get(builder.getContext(),
5656 labelPrefix + opNameAttr.getValue());
5659 auto opName = op->getName().stripDialect();
5660 if (opName ==
"assert") {
5661 verif::ClockedAssertOp::create(builder, predicate, edge, clock, enable,
5665 if (opName ==
"assume") {
5666 verif::ClockedAssumeOp::create(builder, predicate, edge, clock, enable,
5670 if (opName ==
"cover") {
5671 verif::ClockedCoverOp::create(builder, predicate, edge, clock, enable,
5675 llvm_unreachable(
"unknown verification op");
5696LogicalResult FIRRTLLowering::lowerVerificationStatement(
5697 Operation *op, StringRef labelPrefix, Value opClock, Value opPredicate,
5698 Value opEnable, StringAttr opMessageAttr, ValueRange opOperands,
5699 StringAttr opNameAttr,
bool isConcurrent, EventControl opEventControl) {
5700 if (circuitState.lowerToCore)
5701 return lowerVerificationStatementToCore(op, labelPrefix, opClock,
5702 opPredicate, opEnable, opNameAttr,
5705 StringRef opName = op->getName().stripDialect();
5708 ArrayRef<Attribute> guards{};
5709 if (
auto guardsAttr = op->template getAttrOfType<ArrayAttr>(
"guards"))
5710 guards = guardsAttr.getValue();
5712 auto isCover = isa<CoverOp>(op);
5713 auto clock = getLoweredNonClockValue(opClock);
5714 auto enable = getLoweredValue(opEnable);
5715 auto predicate = getLoweredValue(opPredicate);
5716 if (!clock || !enable || !predicate)
5720 if (opNameAttr && !opNameAttr.getValue().empty())
5722 StringAttr prefixedLabel;
5725 StringAttr::get(builder.getContext(), labelPrefix + label.getValue());
5728 SmallVector<Value> messageOps;
5732 if (flavor == VerificationFlavor::IfElseFatal && !isa<AssertOp>(op))
5733 flavor = VerificationFlavor::None;
5735 if (flavor == VerificationFlavor::None) {
5739 auto format = op->getAttrOfType<StringAttr>(
"format");
5741 if (isConcurrent && format && format.getValue() ==
"ifElseFatal") {
5742 if (!isa<AssertOp>(op))
5743 return op->emitError()
5744 <<
"ifElseFatal format cannot be used for non-assertions";
5745 flavor = VerificationFlavor::IfElseFatal;
5746 }
else if (isConcurrent)
5747 flavor = VerificationFlavor::SVA;
5749 flavor = VerificationFlavor::Immediate;
5752 if (!isCover && opMessageAttr && !opMessageAttr.getValue().empty()) {
5756 opOperands, message)))
5759 if (failed(loweredFmtOperands(opOperands, messageOps)))
5762 if (flavor == VerificationFlavor::SVA) {
5767 for (
auto &loweredValue : messageOps)
5768 loweredValue =
sv::SampledOp::create(builder, loweredValue);
5774 case VerificationFlavor::Immediate: {
5776 auto deferImmediate = circt::sv::DeferAssertAttr::get(
5777 builder.getContext(), circt::sv::DeferAssert::Immediate);
5778 addToAlwaysBlock(clock, [&]() {
5779 addIfProceduralBlock(enable, [&]() {
5781 prefixedLabel, message, messageOps);
5786 case VerificationFlavor::IfElseFatal: {
5787 assert(isa<AssertOp>(op) &&
"only assert is expected");
5790 auto boolType = IntegerType::get(builder.getContext(), 1);
5791 predicate = comb::createOrFoldNot(builder, predicate,
true);
5792 predicate = builder.createOrFold<
comb::AndOp>(enable, predicate,
true);
5794 circuitState.addMacroDecl(builder.getStringAttr(
"SYNTHESIS"));
5795 addToIfDefBlock(
"SYNTHESIS", {}, [&]() {
5796 addToAlwaysBlock(clock, [&]() {
5797 addIfProceduralBlock(predicate, [&]() {
5798 circuitState.usedStopCond =
true;
5799 circuitState.addFragment(theModule,
"STOP_COND_FRAGMENT");
5801 circuitState.usedAssertVerboseCond =
true;
5802 circuitState.addFragment(theModule,
"ASSERT_VERBOSE_COND_FRAGMENT");
5804 addIfProceduralBlock(
5805 sv::MacroRefExprOp::create(builder, boolType,
5806 "ASSERT_VERBOSE_COND_"),
5808 sv::ErrorProceduralOp::create(builder, message, messageOps);
5810 addIfProceduralBlock(
5811 sv::MacroRefExprOp::create(builder, boolType,
"STOP_COND_"),
5812 [&]() { sv::FatalProceduralOp::create(builder); });
5818 case VerificationFlavor::SVA: {
5823 comb::createOrFoldNot(builder, enable,
true);
5825 builder.createOrFold<
comb::OrOp>(notEnable, predicate,
true);
5827 predicate = builder.createOrFold<
comb::AndOp>(enable, predicate,
true);
5831 sv::EventControl event;
5832 switch (opEventControl) {
5833 case EventControl::AtPosEdge:
5834 event = circt::sv::EventControl::AtPosEdge;
5836 case EventControl::AtEdge:
5837 event = circt::sv::EventControl::AtEdge;
5839 case EventControl::AtNegEdge:
5840 event = circt::sv::EventControl::AtNegEdge;
5846 circt::sv::EventControlAttr::get(builder.getContext(), event), clock,
5847 predicate, prefixedLabel, message, messageOps);
5850 case VerificationFlavor::None:
5852 "flavor `None` must be converted into one of concreate flavors");
5859 return emitGuards(op->getLoc(), guards,
emit);
5863LogicalResult FIRRTLLowering::visitStmt(AssertOp op) {
5864 return lowerVerificationStatement(
5865 op,
"assert__", op.getClock(), op.getPredicate(), op.getEnable(),
5866 op.getMessageAttr(), op.getSubstitutions(), op.getNameAttr(),
5867 op.getIsConcurrent(), op.getEventControl());
5871LogicalResult FIRRTLLowering::visitStmt(AssumeOp op) {
5872 return lowerVerificationStatement(
5873 op,
"assume__", op.getClock(), op.getPredicate(), op.getEnable(),
5874 op.getMessageAttr(), op.getSubstitutions(), op.getNameAttr(),
5875 op.getIsConcurrent(), op.getEventControl());
5879LogicalResult FIRRTLLowering::visitStmt(CoverOp op) {
5880 return lowerVerificationStatement(
5881 op,
"cover__", op.getClock(), op.getPredicate(), op.getEnable(),
5882 op.getMessageAttr(), op.getSubstitutions(), op.getNameAttr(),
5883 op.getIsConcurrent(), op.getEventControl());
5887LogicalResult FIRRTLLowering::visitStmt(UnclockedAssumeIntrinsicOp op) {
5888 if (circuitState.lowerToCore) {
5889 auto guardsAttr = op->getAttrOfType<mlir::ArrayAttr>(
"guards");
5890 if (guardsAttr && !guardsAttr.empty())
5891 return op.emitOpError(
5892 "lower-to-core does not support guarded verification statements");
5894 auto predicate = getLoweredValue(op.getPredicate());
5895 auto enable = getLoweredValue(op.getEnable());
5896 if (!predicate || !enable)
5899 auto label = op.getNameAttr();
5900 StringAttr assumeLabel;
5901 if (label && !label.empty())
5903 StringAttr::get(builder.getContext(),
"assume__" + label.getValue());
5904 verif::AssumeOp::create(builder, predicate, enable, assumeLabel);
5912 auto guardsAttr = op->getAttrOfType<mlir::ArrayAttr>(
"guards");
5913 ArrayRef<Attribute> guards =
5914 guardsAttr ? guardsAttr.getValue() : ArrayRef<Attribute>();
5916 auto label = op.getNameAttr();
5917 StringAttr assumeLabel;
5918 if (label && !label.empty())
5920 StringAttr::get(builder.getContext(),
"assume__" + label.getValue());
5921 auto predicate = getLoweredValue(op.getPredicate());
5922 auto enable = getLoweredValue(op.getEnable());
5923 auto notEnable = comb::createOrFoldNot(builder, enable,
true);
5924 predicate = builder.createOrFold<
comb::OrOp>(notEnable, predicate,
true);
5926 SmallVector<Value> messageOps;
5927 for (
auto operand : op.getSubstitutions()) {
5928 auto loweredValue = getLoweredValue(operand);
5929 if (!loweredValue) {
5933 loweredValue = getOrCreateIntConstant(1, 0);
5935 messageOps.push_back(loweredValue);
5937 return emitGuards(op.getLoc(), guards, [&]() {
5938 sv::AlwaysOp::create(
5939 builder, ArrayRef(sv::EventControl::AtEdge), ArrayRef(predicate),
5941 if (op.getMessageAttr().getValue().empty())
5942 buildImmediateVerifOp(
5943 builder,
"assume", predicate,
5944 circt::sv::DeferAssertAttr::get(
5945 builder.getContext(), circt::sv::DeferAssert::Immediate),
5948 buildImmediateVerifOp(
5949 builder,
"assume", predicate,
5950 circt::sv::DeferAssertAttr::get(
5951 builder.getContext(), circt::sv::DeferAssert::Immediate),
5952 assumeLabel, op.getMessageAttr(), messageOps);
5957LogicalResult FIRRTLLowering::visitStmt(AttachOp op) {
5959 if (op.getAttached().size() < 2)
5962 SmallVector<Value, 4> inoutValues;
5963 for (
auto v : op.getAttached()) {
5964 inoutValues.push_back(getPossiblyInoutLoweredValue(v));
5965 if (!inoutValues.back()) {
5969 inoutValues.pop_back();
5973 if (!isa<hw::InOutType>(inoutValues.back().getType()))
5974 return op.emitError(
"operand isn't an inout type");
5977 if (inoutValues.size() < 2)
5985 if (circuitState.lowerToCore)
5986 return op.emitOpError(
5987 "lower-to-core does not support firrtl.attach that requires SV "
5993 bool isAttachInternalOnly =
5994 llvm::none_of(inoutValues, [](
auto v) {
return isa<BlockArgument>(v); });
5996 if (isAttachInternalOnly) {
5997 auto v0 = inoutValues.front();
5998 for (
auto v : inoutValues) {
6001 v.replaceAllUsesWith(v0);
6008 circuitState.addMacroDecl(builder.getStringAttr(
"SYNTHESIS"));
6009 circuitState.addMacroDecl(builder.getStringAttr(
"VERILATOR"));
6014 SmallVector<Value, 4> values;
6015 for (
auto inoutValue : inoutValues)
6016 values.push_back(getReadValue(inoutValue));
6018 for (
size_t i1 = 0, e = inoutValues.size(); i1 != e; ++i1) {
6019 for (
size_t i2 = 0; i2 != e; ++i2)
6027 sv::IfDefOp::create(
6028 builder,
"VERILATOR",
6030 sv::VerbatimOp::create(
6032 "`error \"Verilator does not support alias and thus "
6034 "arbitrarily connect bidirectional wires and ports\"");
6036 [&]() { sv::AliasOp::create(builder, inoutValues); });
6042LogicalResult FIRRTLLowering::visitStmt(BindOp op) {
6043 sv::BindOp::create(builder, op.getInstanceAttr());
6047LogicalResult FIRRTLLowering::fixupLTLOps() {
6048 if (ltlOpFixupWorklist.empty())
6050 LLVM_DEBUG(llvm::dbgs() <<
"Fixing up " << ltlOpFixupWorklist.size()
6054 for (
unsigned i = 0, e = ltlOpFixupWorklist.size(); i != e; ++i)
6055 for (
auto *user : ltlOpFixupWorklist[i]->getUsers())
6056 if (isa<
hw::WireOp>(user))
6057 ltlOpFixupWorklist.insert(user);
6060 while (!ltlOpFixupWorklist.empty()) {
6061 auto *op = ltlOpFixupWorklist.pop_back_val();
6064 if (
auto opIntf = dyn_cast_or_null<mlir::InferTypeOpInterface>(op)) {
6065 LLVM_DEBUG(llvm::dbgs() <<
"- Update " << *op <<
"\n");
6066 SmallVector<Type, 2> types;
6067 auto result = opIntf.inferReturnTypes(
6068 op->getContext(), op->getLoc(), op->getOperands(),
6069 op->getAttrDictionary(), op->getPropertiesStorage(), op->getRegions(),
6073 assert(types.size() == op->getNumResults());
6077 for (
auto [result, type] :
llvm::zip(op->getResults(), types)) {
6078 if (result.getType() == type)
6080 LLVM_DEBUG(llvm::dbgs()
6081 <<
" - Result #" << result.getResultNumber() <<
" from "
6082 << result.getType() <<
" to " << type <<
"\n");
6083 result.setType(type);
6084 for (
auto *user : result.getUsers())
6086 ltlOpFixupWorklist.insert(user);
6091 if (
auto wireOp = dyn_cast<hw::WireOp>(op)) {
6092 if (isa<ltl::SequenceType, ltl::PropertyType>(wireOp.getType())) {
6093 wireOp.replaceAllUsesWith(wireOp.getInput());
6094 LLVM_DEBUG(llvm::dbgs() <<
"- Remove " << wireOp <<
"\n");
6095 if (wireOp.use_empty())
6102 SmallPtrSet<Operation *, 4> usersReported;
6103 for (
auto *user : op->getUsers()) {
6104 if (!usersReported.insert(user).second)
6106 if (isa_and_nonnull<ltl::LTLDialect, verif::VerifDialect>(
6107 user->getDialect()))
6109 if (isa<hw::WireOp>(user))
6111 auto d = op->emitError(
6112 "verification operation used in a non-verification context");
6113 d.attachNote(user->getLoc())
6114 <<
"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 verif::ClockEdge firrtlToVerifClockEdge(EventControl eventControl)
static void tryCopyName(Operation *dst, Operation *src)
static LogicalResult verifyOpLegality(Operation *op)
This verifies that the target operation has been lowered to a legal operation.
static Value castFromFIRRTLType(Value val, Type type, ImplicitLocOpBuilder &builder)
Cast from a FIRRTL type (potentially with a flip) to a standard type.
static SmallVector< SubfieldOp > getAllFieldAccesses(Value structValue, StringRef field)
static Value tryEliminatingConnectsToValue(Value flipValue, Operation *insertPoint, CircuitLoweringState &loweringState)
Given a value of flip type, check to see if all of the uses of it are connects.
static LogicalResult resolveFormatString(Location loc, StringRef originalFormatString, ValueRange operands, StringAttr &result)
static Value getSingleNonInstanceOperand(AttachOp op)
static IntType getWidestIntType(Type t1, Type t2)
Given two FIRRTL integer types, return the widest one.
static FailureOr< VectorizeOp > lowerBody(VectorizeOp op)
Vectorizes the body of the given arc.vectorize operation if it is not already vectorized.
static Location getLoc(DefSlot slot)
static StringAttr getArgName(Operation *op, size_t idx)
static Block * getBodyBlock(FModuleLike mod)
std::shared_ptr< calyx::CalyxLoweringState > loweringState
Instantiate one of these and use it to build typed backedges.
void abandon()
Abandon the backedges, suppressing any diagnostics if they are still active upon destruction of the b...
Backedge get(mlir::Type resultType, mlir::LocationAttr optionalLoc={})
Create a typed backedge.
mlir::LogicalResult clearOrEmitError()
Clear the backedges, erasing any remaining cursor ops.
Backedge is a wrapper class around a Value.
void setValue(mlir::Value)
A namespace that is used to store existing names and generate new names in some scope within the IR.
This class provides a read-only projection over the MLIR attributes that represent a set of annotatio...
bool removeAnnotations(llvm::function_ref< bool(Annotation)> predicate)
Remove all annotations from this annotation set for which predicate returns true.
bool removeAnnotation(Annotation anno)
Remove an annotation from this annotation set.
Annotation getAnnotation(StringRef className) const
If this annotation set has an annotation with the specified class name, return it.
This class provides a read-only projection of an annotation.
DictionaryAttr getDict() const
Get the data dictionary of this attribute.
AttrClass getMember(StringAttr name) const
Return a member of the annotation.
bool isClass(Args... names) const
Return true if this annotation matches any of the specified class names.
FIRRTLVisitor allows you to visit all of the expr/stmt/decls with one class declaration.
ResultType visitInvalidOp(Operation *op, ExtraArgs... args)
visitInvalidOp is an override point for non-FIRRTL dialect operations.
ResultType visitUnhandledOp(Operation *op, ExtraArgs... args)
visitUnhandledOp is an override point for FIRRTL dialect ops that the concrete visitor didn't bother ...
This graph tracks modules and where they are instantiated.
FModuleLike getTopLevelModule()
Get the module corresponding to the top-level module of a circuit.
This is the common base class between SIntType and UIntType.
This table tracks nlas and what modules participate in them.
The target of an inner symbol, the entity the symbol is a handle for.
This is an edge in the InstanceGraph.
create(elements, Type result_type=None)
create(str sym_name, Type type, str verilog_name=None)
create(data_type, name=None, sym_name=None)
Direction get(bool isOutput)
Returns an output direction if isOutput is true, otherwise returns an input direction.
StringRef getFragmentsAttrName()
Return the name of the fragments array attribute.
FIRRTLBaseType getBaseType(Type type)
If it is a base type, return it as is.
std::pair< hw::InnerSymAttr, StringAttr > getOrAddInnerSym(MLIRContext *context, hw::InnerSymAttr attr, uint64_t fieldID, llvm::function_ref< hw::InnerSymbolNamespace &()> getNamespace)
Ensure that the the InnerSymAttr has a symbol on the field specified.
bool hasDroppableName(Operation *op)
Return true if the name is droppable.
Type lowerType(Type type, std::optional< Location > loc={}, llvm::function_ref< hw::TypeAliasType(Type, BaseTypeAliasType, Location)> getTypeDeclFn={})
Given a type, return the corresponding lowered type for the HW dialect.
bool isExpression(Operation *op)
Return true if the specified operation is a firrtl expression.
std::optional< int64_t > getBitWidth(FIRRTLBaseType type, bool ignoreFlip=false)
StringAttr getName(ArrayAttr names, size_t idx)
Return the name at the specified index of the ArrayAttr or null if it cannot be determined.
void setSVAttributes(mlir::Operation *op, mlir::ArrayAttr attrs)
Set the SV attributes of an operation.
The InstanceGraph op interface, see InstanceGraphInterface.td for more details.
std::unique_ptr< mlir::Pass > createLowerFIRRTLToHWPass(bool enableAnnotationWarning=false, firrtl::VerificationFlavor assertionFlavor=firrtl::VerificationFlavor::None, bool lowerToCore=false)
This is the pass constructor.
int run(Type[Generator] generator=CppGenerator, 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