36#include "mlir/IR/BuiltinOps.h"
37#include "mlir/IR/BuiltinTypes.h"
38#include "mlir/IR/ImplicitLocOpBuilder.h"
39#include "mlir/IR/Threading.h"
40#include "mlir/Pass/Pass.h"
41#include "llvm/ADT/DenseMap.h"
42#include "llvm/Support/Debug.h"
43#include "llvm/Support/Mutex.h"
44#include "llvm/Support/Path.h"
46#define DEBUG_TYPE "lower-to-hw"
49#define GEN_PASS_DEF_LOWERFIRRTLTOHW
50#include "circt/Conversion/Passes.h.inc"
54using namespace firrtl;
55using circt::comb::ICmpPredicate;
64 auto ftype = dyn_cast<FIRRTLBaseType>(type);
65 return ftype && ftype.getPassiveType().getBitWidthOrSentinel() == 0;
72 for (
auto operand : op.getAttached()) {
74 operand.getDefiningOp<InstanceOp>())
78 if (!operand.hasOneUse() || singleSource)
80 singleSource = operand;
89 auto checkTypes = [](Operation *op) -> WalkResult {
91 if (isa_and_nonnull<FIRRTLDialect>(op->getDialect()))
92 return op->emitError(
"Found unhandled FIRRTL operation '")
93 << op->getName() <<
"'";
96 auto checkTypeRange = [&](TypeRange types) -> LogicalResult {
97 if (llvm::any_of(types, [](Type type) {
98 return isa<FIRRTLDialect>(type.getDialect());
100 return op->emitOpError(
"found unhandled FIRRTL type");
105 if (failed(checkTypeRange(op->getOperandTypes())) ||
106 failed(checkTypeRange(op->getResultTypes())))
107 return WalkResult::interrupt();
110 for (
auto ®ion : op->getRegions())
111 for (
auto &block : region)
112 if (failed(checkTypeRange(block.getArgumentTypes())))
113 return WalkResult::interrupt();
116 return WalkResult::advance();
119 if (checkTypes(op).wasInterrupted() || op->walk(checkTypes).wasInterrupted())
126 auto t1c = type_cast<IntType>(t1), t2c = type_cast<IntType>(t2);
127 return t2c.getWidth() > t1c.getWidth() ? t2c : t1c;
133 ImplicitLocOpBuilder &builder) {
135 if (BundleType bundle = dyn_cast<BundleType>(type))
136 val = builder.createOrFold<HWStructCastOp>(bundle.getPassiveType(), val);
138 if (type != val.getType())
139 val = mlir::UnrealizedConversionCastOp::create(builder, type, val)
147 ImplicitLocOpBuilder &builder) {
149 if (hw::StructType structTy = dyn_cast<hw::StructType>(type)) {
151 val = mlir::UnrealizedConversionCastOp::create(
153 type_cast<FIRRTLBaseType>(val.getType()).getPassiveType(), val)
155 val = builder.createOrFold<HWStructCastOp>(type, val);
160 mlir::UnrealizedConversionCastOp::create(builder, type, val).getResult(0);
166 return size == 1 ? 1 : llvm::Log2_64_Ceil(size);
172 if (
auto attr = src->getAttrOfType<StringAttr>(
"name"))
173 if (!dst->hasAttr(
"sv.namehint") && !dst->hasAttr(
"name"))
174 dst->setAttr(
"sv.namehint", attr);
180class FileDescriptorInfo {
182 FileDescriptorInfo(StringAttr outputFileName, mlir::ValueRange substitutions)
183 : outputFileFormat(outputFileName), substitutions(substitutions) {
185 substitutions.empty() &&
186 "substitutions must be empty when output file name is empty");
189 FileDescriptorInfo() =
default;
192 bool isSubstitutionRequired()
const {
return !substitutions.empty(); }
195 bool isDefaultFd()
const {
return !outputFileFormat; }
197 StringAttr getOutputFileFormat()
const {
return outputFileFormat; }
198 mlir::ValueRange getSubstitutions()
const {
return substitutions; }
202 StringAttr outputFileFormat = {};
205 mlir::ValueRange substitutions;
215struct FIRRTLModuleLowering;
218struct CircuitLoweringState {
220 std::atomic<bool> usedPrintf{
false};
221 std::atomic<bool> usedAssertVerboseCond{
false};
222 std::atomic<bool> usedStopCond{
false};
223 std::atomic<bool> usedFileDescriptorLib{
false};
225 CircuitLoweringState(CircuitOp circuitOp,
bool enableAnnotationWarning,
229 : circuitOp(circuitOp), instanceGraph(instanceGraph),
230 enableAnnotationWarning(enableAnnotationWarning),
231 verificationFlavor(verificationFlavor), nlaTable(nlaTable),
232 macroTable(macroTable) {
233 auto *
context = circuitOp.getContext();
237 AnnotationSet(circuitOp).getAnnotation(testBenchDirAnnoClass)) {
238 auto dirName = tbAnno.getMember<StringAttr>(
"dirname");
239 testBenchDirectory = hw::OutputFileAttr::getAsDirectory(
240 context, dirName.getValue(),
false,
true);
244 if (
auto module = dyn_cast<FModuleLike>(op)) {
249 instanceChoicesByModuleAndCase.try_emplace(module.getModuleNameAttr());
259 testHarness =
nullptr;
260 }
else if (dut == testHarness) {
261 testHarness =
nullptr;
266 auto inDUT = [&](igraph::ModuleOpInterface child) {
268 if (
auto inst = instRec->getInstance<InstanceOp>())
269 return inst.getLowerToBind() || inst.getDoNotPrint();
272 if (
auto parent = dyn_cast<igraph::ModuleOpInterface>(*dut))
273 return getInstanceGraph().isAncestor(child, parent, isPhony);
276 circuitOp->walk([&](FModuleLike moduleOp) {
278 dutModules.insert(moduleOp);
282 Operation *getNewModule(Operation *oldModule) {
283 auto it = oldToNewModuleMap.find(oldModule);
284 return it != oldToNewModuleMap.end() ? it->second :
nullptr;
287 Operation *getOldModule(Operation *newModule) {
288 auto it = newToOldModuleMap.find(newModule);
289 return it != newToOldModuleMap.end() ? it->second :
nullptr;
292 void recordModuleMapping(Operation *oldFMod, Operation *newHWMod) {
293 oldToNewModuleMap[oldFMod] = newHWMod;
294 newToOldModuleMap[newHWMod] = oldFMod;
299 void processRemainingAnnotations(Operation *op,
const AnnotationSet &annoSet);
305 void addBind(sv::BindOp op) {
306 std::lock_guard<std::mutex> lock(bindsMutex);
312 hw::TypeAliasType getTypeAlias(Type rawType, BaseTypeAliasType firAliasType,
315 auto hwAlias = typeAliases.getTypedecl(firAliasType);
318 assert(!typeAliases.isFrozen() &&
319 "type aliases cannot be generated after its frozen");
320 return typeAliases.addTypedecl(rawType, firAliasType, typeLoc);
323 FModuleLike getDut() {
return dut; }
324 FModuleLike getTestHarness() {
return testHarness; }
330 bool isInDUT(igraph::ModuleOpInterface child) {
331 if (
auto hwModule = dyn_cast<hw::HWModuleOp>(child.getOperation()))
332 child = cast<igraph::ModuleOpInterface>(getOldModule(hwModule));
333 return dutModules.contains(child);
336 hw::OutputFileAttr getTestBenchDirectory() {
return testBenchDirectory; }
341 bool isInTestHarness(igraph::ModuleOpInterface mod) {
return !isInDUT(mod); }
348 Type
lowerType(Type type, Location loc) {
349 return ::lowerType(type, loc,
350 [&](Type rawType, BaseTypeAliasType firrtlType,
351 Location typeLoc) -> hw::TypeAliasType {
352 return getTypeAlias(rawType, firrtlType, typeLoc);
358 llvm::sys::SmartScopedLock<true> lock(verbatimSourcesMutex);
359 auto it = verbatimSourcesByFileName.find(fileName);
360 return it != verbatimSourcesByFileName.end() ? it->second :
nullptr;
365 void registerVerbatimSource(StringRef fileName,
367 llvm::sys::SmartScopedLock<true> lock(verbatimSourcesMutex);
368 verbatimSourcesByFileName[fileName] = verbatimOp;
372 emit::FileOp getEmitFileForFile(StringRef fileName) {
373 llvm::sys::SmartScopedLock<true> lock(emitFilesMutex);
374 auto it = emitFilesByFileName.find(fileName);
375 return it != emitFilesByFileName.end() ? it->second :
nullptr;
380 void registerEmitFile(StringRef fileName, emit::FileOp fileOp) {
381 llvm::sys::SmartScopedLock<true> lock(emitFilesMutex);
382 emitFilesByFileName[fileName] = fileOp;
386 friend struct FIRRTLModuleLowering;
387 friend struct FIRRTLLowering;
388 CircuitLoweringState(
const CircuitLoweringState &) =
delete;
389 void operator=(
const CircuitLoweringState &) =
delete;
392 DenseMap<Operation *, Operation *> oldToNewModuleMap;
395 DenseMap<Operation *, Operation *> newToOldModuleMap;
406 DenseSet<igraph::ModuleOpInterface> dutModules;
410 StringSet<> pendingAnnotations;
411 const bool enableAnnotationWarning;
412 std::mutex annotationPrintingMtx;
418 SmallVector<sv::BindOp> binds;
421 std::mutex bindsMutex;
429 FModuleLike testHarness;
432 hw::OutputFileAttr testBenchDirectory;
436 DenseMap<std::pair<Attribute, Attribute>, Attribute> instanceForceNames;
439 SetVector<StringAttr> macroDeclNames;
440 std::mutex macroDeclMutex;
442 void addMacroDecl(StringAttr name) {
443 std::unique_lock<std::mutex> lock(macroDeclMutex);
444 macroDeclNames.insert(name);
448 struct LoweredInstanceChoice {
449 StringAttr parentModule;
450 FlatSymbolRefAttr instanceMacro;
451 hw::InstanceOp hwInstance;
454 using OptionAndCase = std::pair<StringAttr, StringAttr>;
458 DenseMap<OptionAndCase, SmallVector<LoweredInstanceChoice>>>
459 instanceChoicesByModuleAndCase;
461 void addInstanceChoiceForCase(StringAttr optionName, StringAttr caseName,
462 StringAttr parentModule,
463 FlatSymbolRefAttr instanceMacro,
464 hw::InstanceOp hwInstance) {
465 OptionAndCase innerKey{optionName, caseName};
466 instanceChoicesByModuleAndCase.at(parentModule)[innerKey].push_back(
467 {parentModule, instanceMacro, hwInstance});
472 DenseMap<hw::HWModuleOp, SetVector<Attribute>> fragments;
473 llvm::sys::SmartMutex<true> fragmentsMutex;
476 llvm::sys::SmartScopedLock<true> lock(fragmentsMutex);
477 fragments[module].insert(
478 FlatSymbolRefAttr::get(circuitOp.getContext(), fragment));
493 struct RecordTypeAlias {
495 RecordTypeAlias(CircuitOp c) : circuitOp(c) {}
497 hw::TypeAliasType getTypedecl(BaseTypeAliasType firAlias)
const {
498 auto iter = firrtlTypeToAliasTypeMap.find(firAlias);
499 if (iter != firrtlTypeToAliasTypeMap.end())
504 bool isFrozen() {
return frozen; }
506 void freeze() { frozen =
true; }
508 hw::TypeAliasType addTypedecl(Type rawType, BaseTypeAliasType firAlias,
510 assert(!frozen &&
"Record already frozen, cannot be updated");
513 auto b = ImplicitLocOpBuilder::atBlockBegin(
515 &circuitOp->getParentRegion()->getBlocks().back());
517 b,
b.getStringAttr(circuitOp.getName() +
"__TYPESCOPE_"));
518 typeScope.getBodyRegion().push_back(
new Block());
520 auto typeName = firAlias.getName();
525 StringAttr::get(typeName.getContext(),
526 typeDeclNamespace.newName(typeName.getValue()));
528 auto typeScopeBuilder =
529 ImplicitLocOpBuilder::atBlockEnd(typeLoc, typeScope.getBodyBlock());
531 typeName, rawType,
nullptr);
532 auto hwAlias = hw::TypeAliasType::get(
533 SymbolRefAttr::get(typeScope.getSymNameAttr(),
534 {FlatSymbolRefAttr::get(typeDecl)}),
536 auto insert = firrtlTypeToAliasTypeMap.try_emplace(firAlias, hwAlias);
537 assert(insert.second &&
"Entry already exists, insert failed");
538 return insert.first->second;
547 DenseMap<Type, hw::TypeAliasType> firrtlTypeToAliasTypeMap;
555 RecordTypeAlias typeAliases = RecordTypeAlias(circuitOp);
558 llvm::StringMap<sv::SVVerbatimSourceOp> verbatimSourcesByFileName;
559 llvm::sys::SmartMutex<true> verbatimSourcesMutex;
562 llvm::StringMap<emit::FileOp> emitFilesByFileName;
563 llvm::sys::SmartMutex<true> emitFilesMutex;
569void CircuitLoweringState::processRemainingAnnotations(
571 if (!enableAnnotationWarning || annoSet.
empty())
573 std::lock_guard<std::mutex> lock(annotationPrintingMtx);
575 for (
auto a : annoSet) {
576 auto inserted = pendingAnnotations.insert(
a.getClass());
577 if (!inserted.second)
598 markDUTAnnoClass, metadataDirAnnoClass, testBenchDirAnnoClass,
603 extractGrandCentralAnnoClass,
606 extractAssertionsAnnoClass, extractAssumptionsAnnoClass,
607 extractCoverageAnnoClass,
611 moduleHierarchyAnnoClass, testHarnessHierarchyAnnoClass,
612 blackBoxTargetDirAnnoClass))
615 mlir::emitWarning(op->getLoc(),
"unprocessed annotation:'" +
a.getClass() +
616 "' still remaining after LowerToHW");
622struct FIRRTLModuleLowering
623 :
public circt::impl::LowerFIRRTLToHWBase<FIRRTLModuleLowering> {
625 void runOnOperation()
override;
626 void setEnableAnnotationWarning() { enableAnnotationWarning =
true; }
628 using LowerFIRRTLToHWBase<FIRRTLModuleLowering>::verificationFlavor;
631 void lowerFileHeader(CircuitOp op, CircuitLoweringState &
loweringState);
632 void emitInstanceChoiceIncludes(mlir::ModuleOp circuit,
634 static void emitInstanceChoiceIncludeFile(
635 OpBuilder &builder, ModuleOp circuit, StringAttr publicModuleName,
636 StringAttr optionName, StringAttr caseName,
637 ArrayRef<CircuitLoweringState::LoweredInstanceChoice> instances,
640 LogicalResult lowerPorts(ArrayRef<PortInfo> firrtlPorts,
641 SmallVectorImpl<hw::PortInfo> &ports,
642 Operation *moduleOp, StringRef moduleName,
644 bool handleForceNameAnnos(FModuleLike oldModule,
AnnotationSet &annos,
646 hw::HWModuleOp lowerModule(FModuleOp oldModule, Block *topLevelModule,
649 getVerbatimSourceForExtModule(FExtModuleOp oldModule, Block *topLevelModule,
651 hw::HWModuleLike lowerExtModule(FExtModuleOp oldModule, Block *topLevelModule,
654 lowerVerbatimExtModule(FExtModuleOp oldModule, Block *topLevelModule,
657 Block *topLevelModule,
661 lowerModulePortsAndMoveBody(FModuleOp oldModule,
hw::HWModuleOp newModule,
665 LogicalResult lowerFormalBody(verif::FormalOp formalOp,
667 LogicalResult lowerSimulationBody(verif::SimulationOp simulationOp,
669 LogicalResult lowerFileBody(emit::FileOp op);
677 bool enableAnnotationWarning,
679 auto pass = std::make_unique<FIRRTLModuleLowering>();
680 if (enableAnnotationWarning)
681 pass->setEnableAnnotationWarning();
682 pass->verificationFlavor = verificationFlavor;
688void FIRRTLModuleLowering::runOnOperation() {
692 auto *topLevelModule = getOperation().getBody();
696 for (
auto &op : *topLevelModule) {
697 if ((circuit = dyn_cast<CircuitOp>(&op)))
704 auto *circuitBody = circuit.getBodyBlock();
708 CircuitLoweringState state(circuit, enableAnnotationWarning,
709 verificationFlavor, getAnalysis<InstanceGraph>(),
710 &getAnalysis<NLATable>(),
711 getAnalysis<InstanceChoiceMacroTable>());
713 SmallVector<Operation *, 32> opsToProcess;
716 state.processRemainingAnnotations(circuit, circuitAnno);
719 for (
auto &op : make_early_inc_range(circuitBody->getOperations())) {
721 TypeSwitch<Operation *, LogicalResult>(&op)
722 .Case<FModuleOp>([&](
auto module) {
723 auto loweredMod = lowerModule(module, topLevelModule, state);
727 state.recordModuleMapping(&op, loweredMod);
728 opsToProcess.push_back(loweredMod);
730 module.walk([&](Operation *op) {
731 for (auto res : op->getResults()) {
733 type_dyn_cast<BaseTypeAliasType>(res.getType()))
734 state.lowerType(aliasType, op->getLoc());
737 return lowerModulePortsAndMoveBody(module, loweredMod, state);
739 .Case<FExtModuleOp>([&](
auto extModule) {
741 lowerExtModule(extModule, topLevelModule, state);
744 state.recordModuleMapping(&op, loweredMod);
747 .Case<FMemModuleOp>([&](
auto memModule) {
749 lowerMemModule(memModule, topLevelModule, state);
752 state.recordModuleMapping(&op, loweredMod);
755 .Case<FormalOp>([&](
auto oldOp) {
756 auto builder = OpBuilder::atBlockEnd(topLevelModule);
757 auto newOp = verif::FormalOp::create(builder, oldOp.getLoc(),
759 oldOp.getParametersAttr());
760 newOp.getBody().emplaceBlock();
761 state.recordModuleMapping(oldOp, newOp);
762 opsToProcess.push_back(newOp);
765 .Case<SimulationOp>([&](
auto oldOp) {
766 auto loc = oldOp.getLoc();
767 auto builder = OpBuilder::atBlockEnd(topLevelModule);
768 auto newOp = verif::SimulationOp::create(
769 builder, loc, oldOp.getNameAttr(), oldOp.getParametersAttr());
770 auto &body = newOp.getRegion().emplaceBlock();
771 body.addArgument(seq::ClockType::get(builder.getContext()), loc);
772 body.addArgument(builder.getI1Type(), loc);
773 state.recordModuleMapping(oldOp, newOp);
774 opsToProcess.push_back(newOp);
777 .Case<emit::FileOp>([&](
auto fileOp) {
778 fileOp->moveBefore(topLevelModule, topLevelModule->end());
779 opsToProcess.push_back(fileOp);
782 .Case<OptionOp, OptionCaseOp>([&](
auto) {
786 .Default([&](Operation *op) {
791 op->moveBefore(topLevelModule, topLevelModule->end());
797 return signalPassFailure();
800 state.typeAliases.freeze();
805 SmallVector<Attribute> dutHierarchyFiles;
806 SmallVector<Attribute> testHarnessHierarchyFiles;
807 circuitAnno.removeAnnotations([&](
Annotation annotation) {
808 if (annotation.
isClass(moduleHierarchyAnnoClass)) {
809 auto file = hw::OutputFileAttr::getFromFilename(
811 annotation.
getMember<StringAttr>(
"filename").getValue(),
813 dutHierarchyFiles.push_back(file);
816 if (annotation.
isClass(testHarnessHierarchyAnnoClass)) {
817 auto file = hw::OutputFileAttr::getFromFilename(
819 annotation.
getMember<StringAttr>(
"filename").getValue(),
823 if (state.getTestHarness())
824 testHarnessHierarchyFiles.push_back(file);
826 dutHierarchyFiles.push_back(file);
832 if (!dutHierarchyFiles.empty())
833 state.getNewModule(state.getDut())
835 ArrayAttr::get(&getContext(), dutHierarchyFiles));
836 if (!testHarnessHierarchyFiles.empty())
837 state.getNewModule(state.getTestHarness())
839 ArrayAttr::get(&getContext(), testHarnessHierarchyFiles));
843 mlir::failableParallelForEach(&getContext(), opsToProcess, [&](
auto op) {
847 return signalPassFailure();
850 for (
auto bind : state.binds) {
855 for (
auto &[module, fragments] : state.fragments)
857 ArrayAttr::
get(&getContext(), fragments.getArrayRef()));
860 for (
auto oldNew : state.oldToNewModuleMap)
861 oldNew.first->erase();
863 if (!state.macroDeclNames.empty()) {
864 ImplicitLocOpBuilder
b(UnknownLoc::get(&getContext()), circuit);
865 for (
auto name : state.macroDeclNames) {
866 sv::MacroDeclOp::create(b, name);
871 lowerFileHeader(circuit, state);
876 emitInstanceChoiceIncludes(getOperation(), state);
883void FIRRTLModuleLowering::lowerFileHeader(CircuitOp op,
884 CircuitLoweringState &state) {
887 ImplicitLocOpBuilder
b(UnknownLoc::get(&getContext()), op);
891 auto emitGuardedDefine = [&](StringRef guard, StringRef defName,
892 StringRef defineTrue =
"",
893 StringRef defineFalse = StringRef()) {
894 if (!defineFalse.data()) {
895 assert(defineTrue.data() &&
"didn't define anything");
897 b, guard, [&]() { sv::MacroDefOp::create(b, defName, defineTrue); });
902 if (defineTrue.data())
903 sv::MacroDefOp::create(b, defName, defineTrue);
905 [&]() { sv::MacroDefOp::create(b, defName, defineFalse); });
910 auto emitGuard = [&](
const char *guard, llvm::function_ref<void(
void)> body) {
912 b, guard, [] {}, body);
915 if (state.usedFileDescriptorLib) {
917 SmallVector<hw::ModulePort> ports;
921 namePort.
name =
b.getStringAttr(
"name");
922 namePort.
type = hw::StringType::get(
b.getContext());
923 namePort.
dir = hw::ModulePort::Direction::Input;
924 ports.push_back(namePort);
928 fdPort.
name =
b.getStringAttr(
"fd");
929 fdPort.
type =
b.getIntegerType(32);
930 fdPort.
dir = hw::ModulePort::Direction::Output;
931 ports.push_back(fdPort);
934 auto moduleType = hw::ModuleType::get(
b.getContext(), ports);
936 SmallVector<NamedAttribute> perArgumentsAttr;
937 perArgumentsAttr.push_back(
938 {sv::FuncOp::getExplicitlyReturnedAttrName(),
b.getUnitAttr()});
940 SmallVector<Attribute> argumentAttr = {
941 DictionaryAttr::get(
b.getContext(), {}),
942 DictionaryAttr::get(
b.getContext(), perArgumentsAttr)};
945 auto func = sv::FuncOp::create(
947 "__circt_lib_logging::FileDescriptor::get", moduleType,
950 {b.getDictionaryAttr({}),
b.getDictionaryAttr(perArgumentsAttr)}),
956 b.getStringAttr(
"__circt_lib_logging::FileDescriptor::get"));
959 sv::MacroDeclOp::create(b,
"__CIRCT_LIB_LOGGING");
961 emit::FragmentOp::create(b,
"CIRCT_LIB_LOGGING_FRAGMENT", [&] {
962 emitGuard(
"SYNTHESIS", [&]() {
963 emitGuard(
"__CIRCT_LIB_LOGGING", [&]() {
964 sv::VerbatimOp::create(b, R
"(// CIRCT Logging Library
965package __circt_lib_logging;
966 class FileDescriptor;
967 static int global_id [string];
968 static function int get(string name);
969 if (global_id.exists(name) == 32'h0) begin
970 global_id[name] = $fopen(name, "w");
971 if (global_id[name] == 32'h0)
972 $error("Failed to open file %s", name);
974 return global_id[name];
980 sv::MacroDefOp::create(b, "__CIRCT_LIB_LOGGING",
"");
986 if (state.usedPrintf) {
987 sv::MacroDeclOp::create(b,
"PRINTF_COND");
988 sv::MacroDeclOp::create(b,
"PRINTF_COND_");
989 emit::FragmentOp::create(b,
"PRINTF_COND_FRAGMENT", [&] {
990 sv::VerbatimOp::create(
991 b,
"\n// Users can define 'PRINTF_COND' to add an extra gate to "
993 emitGuard(
"PRINTF_COND_", [&]() {
994 emitGuardedDefine(
"PRINTF_COND",
"PRINTF_COND_",
"(`PRINTF_COND)",
"1");
999 if (state.usedAssertVerboseCond) {
1000 sv::MacroDeclOp::create(b,
"ASSERT_VERBOSE_COND");
1001 sv::MacroDeclOp::create(b,
"ASSERT_VERBOSE_COND_");
1002 emit::FragmentOp::create(b,
"ASSERT_VERBOSE_COND_FRAGMENT", [&] {
1003 sv::VerbatimOp::create(
1004 b,
"\n// Users can define 'ASSERT_VERBOSE_COND' to add an extra "
1005 "gate to assert error printing.");
1006 emitGuard(
"ASSERT_VERBOSE_COND_", [&]() {
1007 emitGuardedDefine(
"ASSERT_VERBOSE_COND",
"ASSERT_VERBOSE_COND_",
1008 "(`ASSERT_VERBOSE_COND)",
"1");
1013 if (state.usedStopCond) {
1014 sv::MacroDeclOp::create(b,
"STOP_COND");
1015 sv::MacroDeclOp::create(b,
"STOP_COND_");
1016 emit::FragmentOp::create(b,
"STOP_COND_FRAGMENT", [&] {
1017 sv::VerbatimOp::create(
1018 b,
"\n// Users can define 'STOP_COND' to add an extra gate "
1019 "to stop conditions.");
1020 emitGuard(
"STOP_COND_", [&]() {
1021 emitGuardedDefine(
"STOP_COND",
"STOP_COND_",
"(`STOP_COND)",
"1");
1029void FIRRTLModuleLowering::emitInstanceChoiceIncludeFile(
1030 OpBuilder &builder, mlir::ModuleOp circuit, StringAttr publicModuleName,
1031 StringAttr optionName, StringAttr caseName,
1032 ArrayRef<CircuitLoweringState::LoweredInstanceChoice> instances,
1035 if (instances.empty())
1039 SmallString<128> includeFileName;
1041 llvm::raw_svector_ostream os(includeFileName);
1042 os <<
"targets-" << publicModuleName.getValue() <<
"-"
1043 << optionName.getValue() <<
"-" << caseName.getValue() <<
".svh";
1048 emit::FileOp::create(builder, circuit.getLoc(), includeFileName,
1049 circuitNamespace.
newName(includeFileName));
1050 OpBuilder::InsertionGuard g(builder);
1051 builder.setInsertionPointToStart(&
emitFile.getBodyRegion().front());
1055 SmallString<256> headerComment;
1056 llvm::raw_svector_ostream os(headerComment);
1057 os <<
"// Specialization file for public module: "
1058 << publicModuleName.getValue() <<
"\n"
1059 <<
"// Option: " << optionName.getValue()
1060 <<
", Case: " << caseName.getValue() <<
"\n";
1061 emit::VerbatimOp::create(builder, circuit.getLoc(),
1062 builder.getStringAttr(headerComment));
1069 auto optionCaseMacroRef = macroTable.
getMacro(optionName, caseName);
1070 sv::IfDefOp::create(
1071 builder, circuit.getLoc(), optionCaseMacroRef, [&]() {},
1073 sv::MacroDefOp::create(builder, circuit.getLoc(), optionCaseMacroRef);
1077 for (
auto info : instances) {
1078 auto innerSym =
info.hwInstance.getInnerSymAttr();
1079 assert(innerSym &&
"expected instance to have inner symbol");
1081 sv::IfDefOp::create(
1082 builder, circuit.getLoc(),
info.instanceMacro,
1090 SmallString<256> errorMessage;
1091 llvm::raw_svector_ostream os(errorMessage);
1092 os << info.instanceMacro.getAttr().getValue() <<
"_must_not_be_set";
1093 sv::MacroErrorOp::create(builder, circuit.getLoc(),
1094 builder.getStringAttr(errorMessage));
1097 sv::MacroDefOp::create(
1098 builder, circuit.getLoc(), info.instanceMacro,
1099 builder.getStringAttr(
"{{0}}"),
1100 ArrayAttr::get(builder.getContext(),
1101 ArrayRef<Attribute>(hw::InnerRefAttr::get(
1102 info.parentModule, innerSym.getSymName()))));
1107 emitFile->setAttr(
"output_file", hw::OutputFileAttr::getFromFilename(
1108 builder.getContext(), includeFileName,
1114void FIRRTLModuleLowering::emitInstanceChoiceIncludes(
1115 mlir::ModuleOp topLevelModule, CircuitLoweringState &
loweringState) {
1119 OpBuilder builder(&getContext());
1120 builder.setInsertionPointToEnd(topLevelModule.getBody());
1122 circuitNamespace.
add(topLevelModule);
1126 for (
auto module : topLevelModule.getOps<
hw::HWModuleOp>()) {
1127 if (!module.isPublic())
1132 DenseMap<CircuitLoweringState::OptionAndCase,
1133 SmallVector<CircuitLoweringState::LoweredInstanceChoice>>
1138 for (
auto *node :
llvm::post_order(instanceGraph.lookup(module))) {
1139 auto it =
loweringState.instanceChoicesByModuleAndCase.find(
1140 node->getModule().getModuleNameAttr());
1141 if (it ==
loweringState.instanceChoicesByModuleAndCase.end())
1144 for (
auto &[key, instances] : it->second)
1145 choicesInHierarchy[key].
append(instances.begin(), instances.
end());
1150 emitInstanceChoiceIncludeFile(
1151 builder, topLevelModule, module.getModuleNameAttr(),
1153 key.second, choicesInHierarchy.lookup(key),
1159FIRRTLModuleLowering::lowerPorts(ArrayRef<PortInfo> firrtlPorts,
1160 SmallVectorImpl<hw::PortInfo> &ports,
1161 Operation *moduleOp, StringRef moduleName,
1163 ports.reserve(firrtlPorts.size());
1165 size_t numResults = 0;
1166 for (
auto e :
llvm::enumerate(firrtlPorts)) {
1168 size_t portNo = e.index();
1173 if (firrtlPort.
sym.size() > 1 ||
1174 (firrtlPort.
sym.size() == 1 && !firrtlPort.
sym.getSymName()))
1175 return emitError(firrtlPort.
loc)
1176 <<
"cannot lower aggregate port " << firrtlPort.
name
1177 <<
" with field sensitive symbols, HW dialect does not support "
1178 "per field symbols yet.";
1179 hwPort.
setSym(firrtlPort.
sym, moduleOp->getContext());
1181 if (hadDontTouch && !hwPort.
getSym()) {
1182 if (hwPort.
type.isInteger(0)) {
1183 if (enableAnnotationWarning) {
1184 mlir::emitWarning(firrtlPort.
loc)
1185 <<
"zero width port " << hwPort.
name
1186 <<
" has dontTouch annotation, removing anyway";
1192 hw::InnerSymAttr::get(StringAttr::get(
1193 moduleOp->getContext(),
1194 Twine(
"__") + moduleName + Twine(
"__DONTTOUCH__") +
1195 Twine(portNo) + Twine(
"__") + firrtlPort.
name.strref())),
1196 moduleOp->getContext());
1201 moduleOp->emitError(
"cannot lower this port type to HW");
1207 if (hwPort.
type.isInteger(0)) {
1208 auto sym = hwPort.
getSym();
1209 if (sym && !sym.empty()) {
1210 return mlir::emitError(firrtlPort.
loc)
1211 <<
"zero width port " << hwPort.
name
1212 <<
" is referenced by name [" << sym
1213 <<
"] (e.g. in an XMR) but must be removed";
1220 hwPort.
dir = hw::ModulePort::Direction::Output;
1221 hwPort.
argNum = numResults++;
1222 }
else if (firrtlPort.
isInput()) {
1223 hwPort.
dir = hw::ModulePort::Direction::Input;
1224 hwPort.
argNum = numArgs++;
1228 hwPort.
type = hw::InOutType::get(hwPort.
type);
1229 hwPort.
dir = hw::ModulePort::Direction::InOut;
1230 hwPort.
argNum = numArgs++;
1232 hwPort.
loc = firrtlPort.
loc;
1233 ports.push_back(hwPort);
1243 auto params = llvm::map_range(module.getParameters(), [](Attribute a) {
1244 return cast<ParamDeclAttr>(a);
1249 Builder builder(module);
1254 SmallVector<Attribute> newParams;
1255 for (
const ParamDeclAttr &entry : params) {
1256 auto name = entry.getName();
1257 auto type = entry.getType();
1258 auto value = ignoreValues ? Attribute() : entry.getValue();
1260 hw::ParamDeclAttr::get(builder.getContext(), name, type, value);
1261 newParams.push_back(paramAttr);
1263 return builder.getArrayAttr(newParams);
1266bool FIRRTLModuleLowering::handleForceNameAnnos(
1269 bool failed =
false;
1272 if (!anno.
isClass(forceNameAnnoClass))
1275 auto sym = anno.
getMember<FlatSymbolRefAttr>(
"circt.nonlocal");
1282 auto diag = oldModule.emitOpError()
1283 <<
"contains a '" << forceNameAnnoClass
1284 <<
"' that is not a non-local annotation";
1285 diag.attachNote() <<
"the erroneous annotation is '" << anno.
getDict()
1296 auto diag = oldModule.emitOpError()
1297 <<
"contains a '" << forceNameAnnoClass
1298 <<
"' whose non-local symbol, '" << sym
1299 <<
"' does not exist in the circuit";
1300 diag.attachNote() <<
"the erroneous annotation is '" << anno.
getDict();
1313 cast<hw::InnerRefAttr>(nla.getNamepath().getValue().take_back(2)[0]);
1315 {{inst.getModule(), inst.getName()}, anno.
getMember(
"name")});
1316 if (!inserted.second &&
1317 (anno.
getMember(
"name") != (inserted.first->second))) {
1318 auto diag = oldModule.emitError()
1319 <<
"contained multiple '" << forceNameAnnoClass
1320 <<
"' with different names: " << inserted.first->second
1321 <<
" was not " << anno.
getMember(
"name");
1322 diag.attachNote() <<
"the erroneous annotation is '" << anno.
getDict()
1333 FExtModuleOp oldModule, Block *topLevelModule,
1344 SmallVector<PortInfo> firrtlPorts = oldModule.getPorts();
1345 SmallVector<hw::PortInfo, 8> ports;
1346 if (failed(lowerPorts(firrtlPorts, ports, oldModule, oldModule.getName(),
1351 StringRef verilogName;
1352 if (
auto defName = oldModule.getDefname())
1353 verilogName = defName.value();
1355 verilogName = oldModule.getName();
1357 auto builder = OpBuilder::atBlockEnd(topLevelModule);
1359 auto filesAttr = verbatimAnno.
getMember<ArrayAttr>(
"files");
1360 if (!filesAttr || filesAttr.empty()) {
1361 oldModule->emitError(
"VerbatimBlackBoxAnno missing or empty files array");
1366 auto primaryFile = cast<DictionaryAttr>(filesAttr[0]);
1367 auto primaryFileContent = primaryFile.getAs<StringAttr>(
"content");
1368 auto primaryOutputFile = primaryFile.getAs<StringAttr>(
"output_file");
1370 if (!primaryFileContent || !primaryOutputFile) {
1371 oldModule->emitError(
"VerbatimBlackBoxAnno file missing fields");
1375 auto primaryOutputFileAttr = hw::OutputFileAttr::getFromFilename(
1376 builder.getContext(), primaryOutputFile.getValue());
1378 auto primaryFileName = llvm::sys::path::filename(primaryOutputFile);
1379 auto verbatimSource =
loweringState.getVerbatimSourceForFile(primaryFileName);
1382 SmallVector<Attribute> additionalFiles;
1386 for (
size_t i = 1; i < filesAttr.size(); ++i) {
1387 auto file = cast<DictionaryAttr>(filesAttr[i]);
1388 auto content = file.getAs<StringAttr>(
"content");
1389 auto outputFile = file.getAs<StringAttr>(
"output_file");
1390 auto fileName = llvm::sys::path::filename(outputFile);
1392 if (!(content && outputFile)) {
1393 oldModule->emitError(
"VerbatimBlackBoxAnno file missing fields");
1401 auto fileSymbolName = circuitNamespace.
newName(fileName);
1402 emitFile = emit::FileOp::create(builder, oldModule.getLoc(),
1403 outputFile.getValue(), fileSymbolName);
1404 builder.setInsertionPointToStart(&
emitFile.getBodyRegion().front());
1405 emit::VerbatimOp::create(builder, oldModule.getLoc(), content);
1406 builder.setInsertionPointAfter(
emitFile);
1409 auto ext = llvm::sys::path::extension(outputFile.getValue());
1410 bool excludeFromFileList = (ext ==
".h" || ext ==
".vh" || ext ==
".svh");
1411 auto outputFileAttr = hw::OutputFileAttr::getFromFilename(
1412 builder.getContext(), outputFile.getValue(), excludeFromFileList);
1413 emitFile->setAttr(
"output_file", outputFileAttr);
1417 additionalFiles.push_back(FlatSymbolRefAttr::get(
emitFile));
1423 parameters = builder.getArrayAttr({});
1425 if (!verbatimSource) {
1426 verbatimSource = sv::SVVerbatimSourceOp::create(
1427 builder, oldModule.getLoc(),
1428 circuitNamespace.
newName(primaryFileName.str()),
1429 primaryFileContent.getValue(), primaryOutputFileAttr, parameters,
1430 additionalFiles.empty() ?
nullptr
1431 : builder.getArrayAttr(additionalFiles),
1432 builder.getStringAttr(verilogName));
1434 SymbolTable::setSymbolVisibility(
1435 verbatimSource, SymbolTable::getSymbolVisibility(oldModule));
1437 loweringState.registerVerbatimSource(primaryFileName, verbatimSource);
1440 return verbatimSource;
1444FIRRTLModuleLowering::lowerExtModule(FExtModuleOp oldModule,
1445 Block *topLevelModule,
1447 if (
auto verbatimMod =
1448 lowerVerbatimExtModule(oldModule, topLevelModule,
loweringState))
1454 SmallVector<PortInfo> firrtlPorts = oldModule.getPorts();
1455 SmallVector<hw::PortInfo, 8> ports;
1456 if (failed(lowerPorts(firrtlPorts, ports, oldModule, oldModule.getName(),
1460 StringRef verilogName;
1461 if (
auto defName = oldModule.getDefname())
1462 verilogName = defName.value();
1465 auto builder = OpBuilder::atBlockEnd(topLevelModule);
1466 auto nameAttr = builder.getStringAttr(oldModule.getName());
1471 auto newModule = hw::HWModuleExternOp::create(
1472 builder, oldModule.getLoc(), nameAttr, ports, verilogName, parameters);
1473 SymbolTable::setSymbolVisibility(newModule,
1474 SymbolTable::getSymbolVisibility(oldModule));
1476 bool hasOutputPort =
1477 llvm::any_of(firrtlPorts, [&](
auto p) {
return p.isOutput(); });
1478 if (!hasOutputPort &&
1480 internalVerifBlackBoxAnnoClass) &&
1482 newModule->setAttr(
"firrtl.extract.cover.extra", builder.getUnitAttr());
1485 if (
auto extReqs = oldModule.getExternalRequirements();
1486 extReqs && !extReqs.empty())
1487 newModule->setAttr(
"circt.external_requirements", extReqs);
1492 loweringState.processRemainingAnnotations(oldModule, annos);
1497 FExtModuleOp oldModule, Block *topLevelModule,
1502 auto verbatimSource =
1503 getVerbatimSourceForExtModule(oldModule, topLevelModule,
loweringState);
1505 if (!verbatimSource)
1508 SmallVector<PortInfo> firrtlPorts = oldModule.getPorts();
1509 SmallVector<hw::PortInfo, 8> ports;
1510 if (failed(lowerPorts(firrtlPorts, ports, oldModule, oldModule.getName(),
1514 StringRef verilogName;
1515 if (
auto defName = oldModule.getDefname())
1516 verilogName = defName.value();
1518 auto builder = OpBuilder::atBlockEnd(topLevelModule);
1520 auto newModule = sv::SVVerbatimModuleOp::create(
1523 builder.getStringAttr(oldModule.getName()),
1525 FlatSymbolRefAttr::get(verbatimSource),
1526 parameters ? parameters : builder.getArrayAttr({}),
1527 verilogName.empty() ? StringAttr{}
1528 : builder.getStringAttr(verilogName));
1530 SymbolTable::setSymbolVisibility(newModule,
1531 SymbolTable::getSymbolVisibility(oldModule));
1533 bool hasOutputPort =
1534 llvm::any_of(firrtlPorts, [&](
auto p) {
return p.isOutput(); });
1535 if (!hasOutputPort &&
1537 internalVerifBlackBoxAnnoClass) &&
1539 newModule->setAttr(
"firrtl.extract.cover.extra", builder.getUnitAttr());
1542 if (
auto extReqs = oldModule.getExternalRequirements();
1543 extReqs && !extReqs.empty())
1544 newModule->setAttr(
"circt.external_requirements", extReqs);
1549 loweringState.processRemainingAnnotations(oldModule, annos);
1554FIRRTLModuleLowering::lowerMemModule(FMemModuleOp oldModule,
1555 Block *topLevelModule,
1558 SmallVector<PortInfo> firrtlPorts = oldModule.getPorts();
1559 SmallVector<hw::PortInfo, 8> ports;
1560 if (failed(lowerPorts(firrtlPorts, ports, oldModule, oldModule.getName(),
1565 auto builder = OpBuilder::atBlockEnd(topLevelModule);
1566 auto newModule = hw::HWModuleExternOp::create(
1567 builder, oldModule.getLoc(), oldModule.getModuleNameAttr(), ports,
1568 oldModule.getModuleNameAttr());
1576FIRRTLModuleLowering::lowerModule(FModuleOp oldModule, Block *topLevelModule,
1579 SmallVector<PortInfo> firrtlPorts = oldModule.getPorts();
1580 SmallVector<hw::PortInfo, 8> ports;
1581 if (failed(lowerPorts(firrtlPorts, ports, oldModule, oldModule.getName(),
1586 auto builder = OpBuilder::atBlockEnd(topLevelModule);
1587 auto nameAttr = builder.getStringAttr(oldModule.getName());
1589 hw::HWModuleOp::create(builder, oldModule.getLoc(), nameAttr, ports);
1591 if (
auto comment = oldModule->getAttrOfType<StringAttr>(
"comment"))
1592 newModule.setCommentAttr(comment);
1595 SmallVector<StringRef, 13> attrNames = {
1596 "annotations",
"convention",
"layers",
1597 "portNames",
"sym_name",
"portDirections",
1598 "portTypes",
"portAnnotations",
"portSymbols",
1599 "portLocations",
"parameters", SymbolTable::getVisibilityAttrName(),
1602 DenseSet<StringRef> attrSet(attrNames.begin(), attrNames.end());
1603 SmallVector<NamedAttribute> newAttrs(newModule->getAttrs());
1605 llvm::make_filter_range(oldModule->getAttrs(), [&](auto namedAttr) {
1606 return !attrSet.count(namedAttr.getName()) &&
1607 !newModule->getAttrDictionary().contains(namedAttr.getName());
1609 newAttrs.push_back(i);
1611 newModule->setAttrs(newAttrs);
1615 SymbolTable::setSymbolVisibility(newModule,
1616 SymbolTable::getSymbolVisibility(oldModule));
1622 newModule->setAttr(
"firrtl.extract.cover.extra", builder.getUnitAttr());
1626 if (
auto testBenchDir =
loweringState.getTestBenchDirectory())
1628 if (!newModule->hasAttr(
"output_file"))
1629 newModule->setAttr(
"output_file", testBenchDir);
1630 newModule->setAttr(
"firrtl.extract.do_not_extract",
1631 builder.getUnitAttr());
1632 newModule.setCommentAttr(
1633 builder.getStringAttr(
"VCS coverage exclude_file"));
1639 loweringState.processRemainingAnnotations(oldModule, annos);
1648 Operation *insertPoint) {
1649 if (!value.hasOneUse())
1652 auto attach = dyn_cast<AttachOp>(*value.user_begin());
1653 if (!attach || attach.getNumOperands() != 2)
1657 auto loweredType =
lowerType(value.getType());
1658 if (loweredType.isInteger(0))
1663 auto attachedValue = attach.getOperand(attach.getOperand(0) == value);
1664 auto *op = attachedValue.getDefiningOp();
1665 if (op && op->getBlock() == insertPoint->getBlock() &&
1666 !op->isBeforeInBlock(insertPoint))
1671 ImplicitLocOpBuilder builder(insertPoint->getLoc(), insertPoint);
1687 if (type_isa<AnalogType>(flipValue.getType()))
1690 Operation *connectOp =
nullptr;
1691 for (
auto &use : flipValue.getUses()) {
1694 if (use.getOperandNumber() != 0)
1696 if (!isa<ConnectOp, MatchingConnectOp>(use.getOwner()))
1702 connectOp = use.getOwner();
1712 loweringState.lowerType(flipValue.getType(), flipValue.getLoc());
1713 if (loweredType.isInteger(0))
1718 ImplicitLocOpBuilder builder(insertPoint->getLoc(), insertPoint);
1720 auto connectSrc = connectOp->getOperand(1);
1723 if (!isa<FIRRTLType>(connectSrc.getType())) {
1729 if (!type_cast<FIRRTLBaseType>(connectSrc.getType()).isPassive())
1731 mlir::UnrealizedConversionCastOp::create(
1733 type_cast<FIRRTLBaseType>(connectSrc.getType()).getPassiveType(),
1739 auto destTy = type_cast<FIRRTLBaseType>(flipValue.getType()).getPassiveType();
1741 if (destTy != connectSrc.getType() &&
1742 (isa<BaseTypeAliasType>(connectSrc.getType()) ||
1743 isa<BaseTypeAliasType>(destTy))) {
1745 builder.createOrFold<BitCastOp>(flipValue.getType(), connectSrc);
1747 if (!destTy.isGround()) {
1749 if (destTy != type_cast<FIRRTLType>(connectSrc.getType()))
1751 }
else if (destTy.getBitWidthOrSentinel() !=
1752 type_cast<FIRRTLBaseType>(connectSrc.getType())
1753 .getBitWidthOrSentinel()) {
1756 auto destWidth = destTy.getBitWidthOrSentinel();
1757 assert(destWidth != -1 &&
"must know integer widths");
1758 connectSrc = builder.createOrFold<PadPrimOp>(destTy, connectSrc, destWidth);
1770 SmallVector<SubfieldOp> accesses;
1771 for (
auto *op : structValue.getUsers()) {
1772 assert(isa<SubfieldOp>(op));
1773 auto fieldAccess = cast<SubfieldOp>(op);
1775 fieldAccess.getInput().getType().base().getElementIndex(field);
1776 if (elemIndex && *elemIndex == fieldAccess.getFieldIndex())
1777 accesses.push_back(fieldAccess);
1785LogicalResult FIRRTLModuleLowering::lowerModulePortsAndMoveBody(
1788 ImplicitLocOpBuilder bodyBuilder(oldModule.getLoc(), newModule.getBody());
1795 bodyBuilder.setInsertionPoint(cursor);
1798 SmallVector<PortInfo> firrtlPorts = oldModule.getPorts();
1799 assert(oldModule.getBody().getNumArguments() == firrtlPorts.size() &&
1800 "port count mismatch");
1802 SmallVector<Value, 4> outputs;
1805 auto *outputOp = newModule.getBodyBlock()->getTerminator();
1806 ImplicitLocOpBuilder outputBuilder(oldModule.getLoc(), outputOp);
1808 unsigned nextHWInputArg = 0;
1809 int hwPortIndex = -1;
1810 for (
auto [firrtlPortIndex, port] :
llvm::enumerate(firrtlPorts)) {
1812 auto oldArg = oldModule.getBody().getArgument(firrtlPortIndex);
1815 type_isa<FIRRTLBaseType>(port.type) &&
1816 type_cast<FIRRTLBaseType>(port.type).getBitWidthOrSentinel() == 0;
1820 if (!port.isOutput() && !isZeroWidth) {
1823 Value newArg = newModule.getBody().getArgument(nextHWInputArg++);
1829 oldArg.replaceAllUsesWith(newArg);
1835 if (isZeroWidth && port.isInput()) {
1837 WireOp::create(bodyBuilder, port.type,
1838 "." + port.getName().str() +
".0width_input")
1840 oldArg.replaceAllUsesWith(newArg);
1848 outputs.push_back(value);
1849 assert(oldArg.use_empty() &&
"should have removed all uses of oldArg");
1855 auto newArg = WireOp::create(bodyBuilder, port.type,
1856 "." + port.getName().str() +
".output");
1859 oldArg.replaceAllUsesWith(newArg.getResult());
1862 auto resultHWType =
loweringState.lowerType(port.type, port.loc);
1863 if (!resultHWType.isInteger(0)) {
1866 outputs.push_back(output);
1869 if (
auto sym = newModule.getPort(hwPortIndex).getSym()) {
1870 newArg.setInnerSymAttr(sym);
1871 newModule.setPortSymbolAttr(hwPortIndex, {});
1877 outputOp->setOperands(outputs);
1880 auto &oldBlockInstList = oldModule.getBodyBlock()->getOperations();
1881 auto &newBlockInstList = newModule.getBodyBlock()->getOperations();
1882 newBlockInstList.splice(Block::iterator(cursor), oldBlockInstList,
1883 oldBlockInstList.begin(), oldBlockInstList.end());
1894FIRRTLModuleLowering::lowerFormalBody(verif::FormalOp newOp,
1896 auto builder = OpBuilder::atBlockEnd(&newOp.getBody().front());
1901 auto oldOp = cast<FormalOp>(
loweringState.getOldModule(newOp));
1902 auto moduleName = oldOp.getModuleNameAttr().getAttr();
1903 auto oldModule = cast<FModuleOp>(
1904 loweringState.getInstanceGraph().lookup(moduleName)->getModule());
1905 auto newModule = cast<hw::HWModuleOp>(
loweringState.getNewModule(oldModule));
1908 SmallVector<Value> symbolicInputs;
1909 for (
auto arg : newModule.getBody().getArguments())
1910 symbolicInputs.push_back(
1911 verif::SymbolicValueOp::create(builder, arg.
getLoc(), arg.getType()));
1914 hw::InstanceOp::create(builder, newOp.getLoc(), newModule,
1915 newModule.getNameAttr(), symbolicInputs);
1922FIRRTLModuleLowering::lowerSimulationBody(verif::SimulationOp newOp,
1924 auto builder = OpBuilder::atBlockEnd(newOp.getBody());
1927 auto oldOp = cast<SimulationOp>(
loweringState.getOldModule(newOp));
1928 auto moduleName = oldOp.getModuleNameAttr().getAttr();
1929 auto oldModule = cast<FModuleLike>(
1930 *
loweringState.getInstanceGraph().lookup(moduleName)->getModule());
1932 cast<hw::HWModuleLike>(
loweringState.getNewModule(oldModule));
1936 SmallVector<Value> inputs(newOp.getBody()->args_begin(),
1937 newOp.getBody()->args_end());
1938 auto instOp = hw::InstanceOp::create(builder, newOp.getLoc(), newModule,
1939 newModule.getNameAttr(), inputs);
1940 verif::YieldOp::create(builder, newOp.getLoc(), instOp.getResults());
1950struct FIRRTLLowering :
public FIRRTLVisitor<FIRRTLLowering, LogicalResult> {
1952 FIRRTLLowering(
hw::HWModuleOp module, CircuitLoweringState &circuitState)
1953 : theModule(module), circuitState(circuitState),
1954 builder(module.
getLoc(), module.getContext()), moduleNamespace(module),
1955 backedgeBuilder(builder, module.
getLoc()) {}
1957 LogicalResult
run();
1960 Value getOrCreateClockConstant(seq::ClockConst clock);
1961 Value getOrCreateIntConstant(
const APInt &value);
1962 Value getOrCreateIntConstant(
unsigned numBits, uint64_t val,
1963 bool isSigned =
false) {
1964 return getOrCreateIntConstant(APInt(numBits, val, isSigned));
1966 Attribute getOrCreateAggregateConstantAttribute(Attribute value, Type type);
1967 Value getOrCreateXConstant(
unsigned numBits);
1968 Value getOrCreateZConstant(Type type);
1969 Value getPossiblyInoutLoweredValue(Value value);
1970 Value getLoweredValue(Value value);
1971 Value getLoweredNonClockValue(Value value);
1972 Value getLoweredAndExtendedValue(Value value, Type destType);
1973 Value getLoweredAndExtOrTruncValue(Value value, Type destType);
1974 LogicalResult setLowering(Value orig, Value result);
1975 LogicalResult setPossiblyFoldedLowering(Value orig, Value result);
1976 template <
typename ResultOpType,
typename... CtorArgTypes>
1977 LogicalResult setLoweringTo(Operation *orig, CtorArgTypes... args);
1978 template <
typename ResultOpType,
typename... CtorArgTypes>
1979 LogicalResult setLoweringToLTL(Operation *orig, CtorArgTypes... args);
1980 Backedge createBackedge(Location loc, Type type);
1981 Backedge createBackedge(Value orig, Type type);
1982 bool updateIfBackedge(Value dest, Value src);
1985 bool requiresInnerSymbol(hw::InnerSymbolOpInterface op) {
1990 if (
auto forceable = dyn_cast<Forceable>(op.getOperation()))
1991 if (forceable.isForceable())
1999 hw::InnerSymAttr lowerInnerSymbol(hw::InnerSymbolOpInterface op) {
2000 auto attr = op.getInnerSymAttr();
2004 if (requiresInnerSymbol(op))
2006 op.getContext(), attr, 0,
2014 LogicalResult prepareInstanceOperands(ArrayRef<PortInfo> portInfo,
2015 Operation *instanceOp,
2016 SmallVectorImpl<Value> &inputOperands);
2018 void runWithInsertionPointAtEndOfBlock(
const std::function<
void(
void)> &fn,
2022 Value getReadValue(Value v);
2024 Value getNonClockValue(Value v);
2026 void addToAlwaysBlock(sv::EventControl clockEdge, Value clock,
2027 sv::ResetType resetStyle, sv::EventControl resetEdge,
2028 Value reset,
const std::function<
void(
void)> &body = {},
2029 const std::function<void(
void)> &resetBody = {});
2030 void addToAlwaysBlock(Value clock,
2031 const std::function<
void(
void)> &body = {}) {
2032 addToAlwaysBlock(sv::EventControl::AtPosEdge, clock, sv::ResetType(),
2033 sv::EventControl(), Value(), body,
2034 std::function<
void(
void)>());
2037 LogicalResult emitGuards(Location loc, ArrayRef<Attribute> guards,
2038 std::function<
void(
void)>
emit);
2039 void addToIfDefBlock(StringRef cond, std::function<
void(
void)> thenCtor,
2040 std::function<
void(
void)> elseCtor = {});
2041 void addToInitialBlock(std::function<
void(
void)> body);
2042 void addIfProceduralBlock(Value cond, std::function<
void(
void)> thenCtor,
2043 std::function<
void(
void)> elseCtor = {});
2044 Value getExtOrTruncAggregateValue(Value array,
FIRRTLBaseType sourceType,
2046 bool allowTruncate);
2047 Value createArrayIndexing(Value array, Value index);
2048 Value createValueWithMuxAnnotation(Operation *op,
bool isMux2);
2050 using FIRRTLVisitor<FIRRTLLowering, LogicalResult>::visitExpr;
2051 using FIRRTLVisitor<FIRRTLLowering, LogicalResult>::visitDecl;
2052 using FIRRTLVisitor<FIRRTLLowering, LogicalResult>::visitStmt;
2055 enum UnloweredOpResult { AlreadyLowered, NowLowered, LoweringFailure };
2056 UnloweredOpResult handleUnloweredOp(Operation *op);
2057 LogicalResult visitExpr(ConstantOp op);
2058 LogicalResult visitExpr(SpecialConstantOp op);
2059 LogicalResult visitExpr(SubindexOp op);
2060 LogicalResult visitExpr(SubaccessOp op);
2061 LogicalResult visitExpr(SubfieldOp op);
2062 LogicalResult visitExpr(VectorCreateOp op);
2063 LogicalResult visitExpr(BundleCreateOp op);
2064 LogicalResult visitExpr(FEnumCreateOp op);
2065 LogicalResult visitExpr(AggregateConstantOp op);
2066 LogicalResult visitExpr(IsTagOp op);
2067 LogicalResult visitExpr(SubtagOp op);
2068 LogicalResult visitExpr(TagExtractOp op);
2071 if (
auto castOp = dyn_cast<mlir::UnrealizedConversionCastOp>(op))
2072 return visitUnrealizedConversionCast(castOp);
2077 LogicalResult visitDecl(WireOp op);
2078 LogicalResult visitDecl(NodeOp op);
2079 LogicalResult visitDecl(RegOp op);
2080 LogicalResult visitDecl(RegResetOp op);
2081 LogicalResult visitDecl(MemOp op);
2082 LogicalResult visitDecl(InstanceOp oldInstance);
2083 LogicalResult visitDecl(InstanceChoiceOp oldInstanceChoice);
2084 LogicalResult visitDecl(VerbatimWireOp op);
2085 LogicalResult visitDecl(ContractOp op);
2088 LogicalResult lowerNoopCast(Operation *op);
2089 LogicalResult visitExpr(AsSIntPrimOp op);
2090 LogicalResult visitExpr(AsUIntPrimOp op);
2091 LogicalResult visitExpr(AsClockPrimOp op);
2092 LogicalResult visitExpr(AsAsyncResetPrimOp op) {
return lowerNoopCast(op); }
2094 LogicalResult visitExpr(HWStructCastOp op);
2095 LogicalResult visitExpr(BitCastOp op);
2097 visitUnrealizedConversionCast(mlir::UnrealizedConversionCastOp op);
2098 LogicalResult visitExpr(CvtPrimOp op);
2099 LogicalResult visitExpr(NotPrimOp op);
2100 LogicalResult visitExpr(NegPrimOp op);
2101 LogicalResult visitExpr(PadPrimOp op);
2102 LogicalResult visitExpr(XorRPrimOp op);
2103 LogicalResult visitExpr(AndRPrimOp op);
2104 LogicalResult visitExpr(OrRPrimOp op);
2107 template <
typename ResultUnsignedOpType,
2108 typename ResultSignedOpType = ResultUnsignedOpType>
2109 LogicalResult lowerBinOp(Operation *op);
2110 template <
typename ResultOpType>
2111 LogicalResult lowerBinOpToVariadic(Operation *op);
2113 template <
typename ResultOpType>
2114 LogicalResult lowerElementwiseLogicalOp(Operation *op);
2116 LogicalResult lowerCmpOp(Operation *op, ICmpPredicate signedOp,
2117 ICmpPredicate unsignedOp);
2118 template <
typename SignedOp,
typename Un
signedOp>
2119 LogicalResult lowerDivLikeOp(Operation *op);
2121 LogicalResult visitExpr(CatPrimOp op);
2123 LogicalResult visitExpr(AndPrimOp op) {
2124 return lowerBinOpToVariadic<comb::AndOp>(op);
2126 LogicalResult visitExpr(OrPrimOp op) {
2127 return lowerBinOpToVariadic<comb::OrOp>(op);
2129 LogicalResult visitExpr(XorPrimOp op) {
2130 return lowerBinOpToVariadic<comb::XorOp>(op);
2132 LogicalResult visitExpr(ElementwiseOrPrimOp op) {
2133 return lowerElementwiseLogicalOp<comb::OrOp>(op);
2135 LogicalResult visitExpr(ElementwiseAndPrimOp op) {
2136 return lowerElementwiseLogicalOp<comb::AndOp>(op);
2138 LogicalResult visitExpr(ElementwiseXorPrimOp op) {
2139 return lowerElementwiseLogicalOp<comb::XorOp>(op);
2141 LogicalResult visitExpr(AddPrimOp op) {
2142 return lowerBinOpToVariadic<comb::AddOp>(op);
2144 LogicalResult visitExpr(EQPrimOp op) {
2145 return lowerCmpOp(op, ICmpPredicate::eq, ICmpPredicate::eq);
2147 LogicalResult visitExpr(NEQPrimOp op) {
2148 return lowerCmpOp(op, ICmpPredicate::ne, ICmpPredicate::ne);
2150 LogicalResult visitExpr(LTPrimOp op) {
2151 return lowerCmpOp(op, ICmpPredicate::slt, ICmpPredicate::ult);
2153 LogicalResult visitExpr(LEQPrimOp op) {
2154 return lowerCmpOp(op, ICmpPredicate::sle, ICmpPredicate::ule);
2156 LogicalResult visitExpr(GTPrimOp op) {
2157 return lowerCmpOp(op, ICmpPredicate::sgt, ICmpPredicate::ugt);
2159 LogicalResult visitExpr(GEQPrimOp op) {
2160 return lowerCmpOp(op, ICmpPredicate::sge, ICmpPredicate::uge);
2163 LogicalResult visitExpr(SubPrimOp op) {
return lowerBinOp<comb::SubOp>(op); }
2164 LogicalResult visitExpr(MulPrimOp op) {
2165 return lowerBinOpToVariadic<comb::MulOp>(op);
2167 LogicalResult visitExpr(DivPrimOp op) {
2168 return lowerDivLikeOp<comb::DivSOp, comb::DivUOp>(op);
2170 LogicalResult visitExpr(RemPrimOp op) {
2171 return lowerDivLikeOp<comb::ModSOp, comb::ModUOp>(op);
2175 LogicalResult visitExpr(IsXIntrinsicOp op);
2176 LogicalResult visitExpr(PlusArgsTestIntrinsicOp op);
2177 LogicalResult visitExpr(PlusArgsValueIntrinsicOp op);
2178 LogicalResult visitStmt(FPGAProbeIntrinsicOp op);
2179 LogicalResult visitExpr(ClockInverterIntrinsicOp op);
2180 LogicalResult visitExpr(ClockDividerIntrinsicOp op);
2181 LogicalResult visitExpr(SizeOfIntrinsicOp op);
2182 LogicalResult visitExpr(ClockGateIntrinsicOp op);
2183 LogicalResult visitExpr(LTLAndIntrinsicOp op);
2184 LogicalResult visitExpr(LTLOrIntrinsicOp op);
2185 LogicalResult visitExpr(LTLIntersectIntrinsicOp op);
2186 LogicalResult visitExpr(LTLDelayIntrinsicOp op);
2187 LogicalResult visitExpr(LTLConcatIntrinsicOp op);
2188 LogicalResult visitExpr(LTLRepeatIntrinsicOp op);
2189 LogicalResult visitExpr(LTLGoToRepeatIntrinsicOp op);
2190 LogicalResult visitExpr(LTLNonConsecutiveRepeatIntrinsicOp op);
2191 LogicalResult visitExpr(LTLNotIntrinsicOp op);
2192 LogicalResult visitExpr(LTLImplicationIntrinsicOp op);
2193 LogicalResult visitExpr(LTLUntilIntrinsicOp op);
2194 LogicalResult visitExpr(LTLEventuallyIntrinsicOp op);
2195 LogicalResult visitExpr(LTLClockIntrinsicOp op);
2197 template <
typename TargetOp,
typename IntrinsicOp>
2198 LogicalResult lowerVerifIntrinsicOp(IntrinsicOp op);
2199 LogicalResult visitStmt(VerifAssertIntrinsicOp op);
2200 LogicalResult visitStmt(VerifAssumeIntrinsicOp op);
2201 LogicalResult visitStmt(VerifCoverIntrinsicOp op);
2202 LogicalResult visitStmt(VerifRequireIntrinsicOp op);
2203 LogicalResult visitStmt(VerifEnsureIntrinsicOp op);
2204 LogicalResult visitExpr(HasBeenResetIntrinsicOp op);
2205 LogicalResult visitStmt(UnclockedAssumeIntrinsicOp op);
2208 LogicalResult visitExpr(BitsPrimOp op);
2209 LogicalResult visitExpr(InvalidValueOp op);
2210 LogicalResult visitExpr(HeadPrimOp op);
2211 LogicalResult visitExpr(ShlPrimOp op);
2212 LogicalResult visitExpr(ShrPrimOp op);
2213 LogicalResult visitExpr(DShlPrimOp op) {
2214 return lowerDivLikeOp<comb::ShlOp, comb::ShlOp>(op);
2216 LogicalResult visitExpr(DShrPrimOp op) {
2217 return lowerDivLikeOp<comb::ShrSOp, comb::ShrUOp>(op);
2219 LogicalResult visitExpr(DShlwPrimOp op) {
2220 return lowerDivLikeOp<comb::ShlOp, comb::ShlOp>(op);
2222 LogicalResult visitExpr(TailPrimOp op);
2223 LogicalResult visitExpr(MuxPrimOp op);
2224 LogicalResult visitExpr(Mux2CellIntrinsicOp op);
2225 LogicalResult visitExpr(Mux4CellIntrinsicOp op);
2226 LogicalResult visitExpr(MultibitMuxOp op);
2227 LogicalResult visitExpr(VerbatimExprOp op);
2228 LogicalResult visitExpr(XMRRefOp op);
2229 LogicalResult visitExpr(XMRDerefOp op);
2232 LogicalResult visitExpr(TimeOp op);
2233 LogicalResult visitExpr(HierarchicalModuleNameOp op);
2236 LogicalResult lowerVerificationStatement(
2237 Operation *op, StringRef labelPrefix, Value clock, Value predicate,
2238 Value enable, StringAttr messageAttr, ValueRange operands,
2239 StringAttr nameAttr,
bool isConcurrent, EventControl eventControl);
2241 LogicalResult visitStmt(SkipOp op);
2243 FailureOr<bool> lowerConnect(Value dest, Value srcVal);
2244 LogicalResult visitStmt(ConnectOp op);
2245 LogicalResult visitStmt(MatchingConnectOp op);
2246 LogicalResult visitStmt(ForceOp op);
2248 std::optional<Value> getLoweredFmtOperand(Value operand);
2249 LogicalResult loweredFmtOperands(ValueRange operands,
2250 SmallVectorImpl<Value> &loweredOperands);
2251 FailureOr<Value> callFileDescriptorLib(
const FileDescriptorInfo &info);
2255 LogicalResult lowerStatementWithFd(
2256 const FileDescriptorInfo &fileDescriptorInfo, Value clock, Value cond,
2257 const std::function<LogicalResult(Value)> &fn,
bool usePrintfCond);
2261 LogicalResult visitPrintfLike(T op,
2262 const FileDescriptorInfo &fileDescriptorInfo,
2263 bool usePrintfCond);
2264 LogicalResult visitStmt(PrintFOp op) {
return visitPrintfLike(op, {},
true); }
2265 LogicalResult visitStmt(FPrintFOp op);
2266 LogicalResult visitStmt(FFlushOp op);
2267 LogicalResult visitStmt(StopOp op);
2268 LogicalResult visitStmt(AssertOp op);
2269 LogicalResult visitStmt(AssumeOp op);
2270 LogicalResult visitStmt(CoverOp op);
2271 LogicalResult visitStmt(AttachOp op);
2272 LogicalResult visitStmt(RefForceOp op);
2273 LogicalResult visitStmt(RefForceInitialOp op);
2274 LogicalResult visitStmt(RefReleaseOp op);
2275 LogicalResult visitStmt(RefReleaseInitialOp op);
2276 LogicalResult visitStmt(BindOp op);
2278 FailureOr<Value> lowerSubindex(SubindexOp op, Value input);
2279 FailureOr<Value> lowerSubaccess(SubaccessOp op, Value input);
2280 FailureOr<Value> lowerSubfield(SubfieldOp op, Value input);
2282 LogicalResult fixupLTLOps();
2285 return circuitState.lowerType(type, builder.getLoc());
2293 CircuitLoweringState &circuitState;
2296 ImplicitLocOpBuilder builder;
2301 DenseMap<Value, Value> valueMapping;
2305 DenseMap<Value, Value> fromClockMapping;
2309 DenseMap<Attribute, Value> hwConstantMap;
2310 DenseMap<std::pair<Attribute, Type>, Attribute> hwAggregateConstantMap;
2314 DenseMap<unsigned, Value> hwConstantXMap;
2315 DenseMap<Type, Value> hwConstantZMap;
2321 DenseMap<Value, Value> readInOutCreated;
2324 DenseMap<StringAttr, sv::RegOp> fileNameToFileDescriptor;
2328 using AlwaysKeyType = std::tuple<
Block *, sv::EventControl, Value,
2329 sv::ResetType, sv::EventControl, Value>;
2346 llvm::MapVector<Value, Value> backedges;
2353 DenseSet<Operation *> maybeUnusedValues;
2355 void maybeUnused(Operation *op) { maybeUnusedValues.insert(op); }
2356 void maybeUnused(Value value) {
2357 if (
auto *op = value.getDefiningOp())
2369 SetVector<Operation *> ltlOpFixupWorklist;
2374 SmallVector<std::pair<Block::iterator, Block::iterator>> worklist;
2376 void addToWorklist(Block &block) {
2377 worklist.push_back({block.begin(), block.end()});
2379 void addToWorklist(Region ®ion) {
2380 for (
auto &block :
llvm::reverse(region))
2381 addToWorklist(block);
2392LogicalResult FIRRTLModuleLowering::lowerFileBody(emit::FileOp fileOp) {
2393 OpBuilder
b(&getContext());
2394 fileOp->walk([&](Operation *op) {
2395 if (
auto bindOp = dyn_cast<BindOp>(op)) {
2396 b.setInsertionPointAfter(bindOp);
2397 sv::BindOp::create(b, bindOp.getLoc(), bindOp.getInstanceAttr());
2405FIRRTLModuleLowering::lowerBody(Operation *op,
2407 if (
auto moduleOp = dyn_cast<hw::HWModuleOp>(op))
2409 if (
auto formalOp = dyn_cast<verif::FormalOp>(op))
2411 if (
auto simulationOp = dyn_cast<verif::SimulationOp>(op))
2413 if (
auto fileOp = dyn_cast<emit::FileOp>(op))
2414 return lowerFileBody(fileOp);
2419LogicalResult FIRRTLLowering::run() {
2422 for (
auto arg : theModule.
getBodyBlock()->getArguments())
2423 if (failed(setLowering(arg, arg)))
2430 addToWorklist(theModule.getBody());
2431 SmallVector<Operation *, 16> opsToRemove;
2433 while (!worklist.empty()) {
2434 auto &[opsIt, opsEnd] = worklist.back();
2435 if (opsIt == opsEnd) {
2436 worklist.pop_back();
2439 Operation *op = &*opsIt++;
2441 builder.setInsertionPoint(op);
2442 builder.setLoc(op->getLoc());
2443 auto done = succeeded(dispatchVisitor(op));
2444 circuitState.processRemainingAnnotations(op,
AnnotationSet(op));
2446 opsToRemove.push_back(op);
2448 switch (handleUnloweredOp(op)) {
2449 case AlreadyLowered:
2452 opsToRemove.push_back(op);
2454 case LoweringFailure:
2466 for (
auto &[backedge, value] : backedges) {
2467 SmallVector<Location> driverLocs;
2473 if (backedge == value) {
2474 Location edgeLoc = backedge.getLoc();
2475 if (driverLocs.empty()) {
2476 mlir::emitError(edgeLoc,
"sink does not have a driver");
2478 auto diag = mlir::emitError(edgeLoc,
"sink in combinational loop");
2479 for (
auto loc : driverLocs)
2480 diag.attachNote(loc) <<
"through driver here";
2486 auto *it = backedges.find(value);
2487 if (it == backedges.end())
2490 driverLocs.push_back(value.getLoc());
2493 if (
auto *defOp = backedge.getDefiningOp())
2494 maybeUnusedValues.erase(defOp);
2495 backedge.replaceAllUsesWith(value);
2503 while (!opsToRemove.empty()) {
2504 auto *op = opsToRemove.pop_back_val();
2509 for (
auto result : op->getResults()) {
2513 auto builder = OpBuilder::atBlockBegin(theModule.getBodyBlock());
2515 builder.getIntegerType(0), 0);
2516 maybeUnusedValues.insert(zeroI0);
2518 result.replaceAllUsesWith(zeroI0);
2521 if (!op->use_empty()) {
2522 auto d = op->emitOpError(
2523 "still has uses; should remove ops in reverse order of visitation");
2524 SmallPtrSet<Operation *, 2> visited;
2525 for (
auto *user : op->getUsers())
2526 if (visited.insert(user).second)
2528 <<
"used by " << user->
getName() <<
" op";
2531 maybeUnusedValues.erase(op);
2536 while (!maybeUnusedValues.empty()) {
2537 auto it = maybeUnusedValues.begin();
2539 maybeUnusedValues.erase(it);
2540 if (!isOpTriviallyDead(op))
2542 for (
auto operand : op->getOperands())
2543 if (auto *defOp = operand.getDefiningOp())
2544 maybeUnusedValues.insert(defOp);
2550 if (failed(fixupLTLOps()))
2561Value FIRRTLLowering::getOrCreateClockConstant(seq::ClockConst clock) {
2562 auto attr = seq::ClockConstAttr::get(theModule.getContext(), clock);
2564 auto &entry = hwConstantMap[attr];
2568 OpBuilder entryBuilder(&theModule.getBodyBlock()->front());
2569 entry = seq::ConstClockOp::create(entryBuilder, builder.getLoc(), attr);
2575Value FIRRTLLowering::getOrCreateIntConstant(
const APInt &value) {
2576 auto attr = builder.getIntegerAttr(
2577 builder.getIntegerType(value.getBitWidth()), value);
2579 auto &entry = hwConstantMap[attr];
2583 OpBuilder entryBuilder(&theModule.getBodyBlock()->front());
2590Attribute FIRRTLLowering::getOrCreateAggregateConstantAttribute(Attribute value,
2593 if (hw::type_isa<IntegerType>(type))
2594 return builder.getIntegerAttr(type, cast<IntegerAttr>(value).getValue());
2596 auto cache = hwAggregateConstantMap.lookup({value, type});
2601 SmallVector<Attribute> values;
2602 for (
auto e :
llvm::enumerate(cast<ArrayAttr>(value))) {
2604 if (
auto array = hw::type_dyn_cast<hw::ArrayType>(type))
2605 subType = array.getElementType();
2606 else if (
auto structType = hw::type_dyn_cast<hw::StructType>(type))
2607 subType = structType.getElements()[e.index()].type;
2609 assert(
false &&
"type must be either array or struct");
2611 values.push_back(getOrCreateAggregateConstantAttribute(e.value(), subType));
2615 if (hw::type_isa<hw::ArrayType>(type))
2616 std::reverse(values.begin(), values.end());
2618 auto &entry = hwAggregateConstantMap[{value, type}];
2619 entry = builder.getArrayAttr(values);
2627 const std::function<LogicalResult()> &fn) {
2628 assert(failedOperand &&
"Should be called on the failed operand");
2636Value FIRRTLLowering::getOrCreateXConstant(
unsigned numBits) {
2638 auto &entry = hwConstantXMap[numBits];
2642 OpBuilder entryBuilder(&theModule.getBodyBlock()->front());
2643 entry = sv::ConstantXOp::create(entryBuilder, builder.getLoc(),
2644 entryBuilder.getIntegerType(numBits));
2648Value FIRRTLLowering::getOrCreateZConstant(Type type) {
2649 auto &entry = hwConstantZMap[type];
2651 OpBuilder entryBuilder(&theModule.getBodyBlock()->front());
2652 entry = sv::ConstantZOp::create(entryBuilder, builder.getLoc(), type);
2661Value FIRRTLLowering::getPossiblyInoutLoweredValue(Value value) {
2663 if (
auto lowering = valueMapping.lookup(value)) {
2664 assert(!isa<FIRRTLType>(lowering.getType()) &&
2665 "Lowered value should be a non-FIRRTL value");
2674Value FIRRTLLowering::getLoweredValue(Value value) {
2675 auto result = getPossiblyInoutLoweredValue(value);
2681 if (isa<hw::InOutType>(result.getType()))
2682 return getReadValue(result);
2688Value FIRRTLLowering::getLoweredNonClockValue(Value value) {
2689 auto result = getLoweredValue(value);
2693 if (hw::type_isa<seq::ClockType>(result.getType()))
2694 return getNonClockValue(result);
2702Value FIRRTLLowering::getExtOrTruncAggregateValue(Value array,
2705 bool allowTruncate) {
2706 SmallVector<Value> resultBuffer;
2711 auto srcWidth = firrtl::type_cast<IntType>(sourceType).getWidthOrSentinel();
2712 auto destWidth = firrtl::type_cast<IntType>(destType).getWidthOrSentinel();
2713 auto resultType = builder.getIntegerType(destWidth);
2715 if (srcWidth == destWidth)
2718 if (srcWidth > destWidth) {
2722 builder.emitError(
"operand should not be a truncation");
2726 if (firrtl::type_cast<IntType>(sourceType).isSigned())
2727 return comb::createOrFoldSExt(builder, value, resultType);
2728 auto zero = getOrCreateIntConstant(destWidth - srcWidth, 0);
2736 return TypeSwitch<FIRRTLBaseType, LogicalResult>(srcType)
2737 .Case<FVectorType>([&](
auto srcVectorType) {
2738 auto destVectorType = firrtl::type_cast<FVectorType>(destType);
2739 unsigned size = resultBuffer.size();
2740 unsigned indexWidth =
2742 for (
size_t i = 0, e = std::min(srcVectorType.getNumElements(),
2743 destVectorType.getNumElements());
2745 auto iIdx = getOrCreateIntConstant(indexWidth, i);
2747 if (failed(recurse(arrayIndex, srcVectorType.getElementType(),
2748 destVectorType.getElementType())))
2751 SmallVector<Value> temp(resultBuffer.begin() + size,
2752 resultBuffer.end());
2754 resultBuffer.resize(size);
2755 resultBuffer.push_back(array);
2758 .Case<BundleType>([&](BundleType srcStructType) {
2759 auto destStructType = firrtl::type_cast<BundleType>(destType);
2760 unsigned size = resultBuffer.size();
2763 if (destStructType.getNumElements() != srcStructType.getNumElements())
2766 for (
auto elem :
llvm::enumerate(destStructType)) {
2767 auto structExtract =
2769 if (failed(recurse(structExtract,
2770 srcStructType.getElementType(elem.index()),
2771 destStructType.getElementType(elem.index()))))
2774 SmallVector<Value> temp(resultBuffer.begin() + size,
2775 resultBuffer.end());
2778 resultBuffer.resize(size);
2779 resultBuffer.push_back(newStruct);
2782 .Case<IntType>([&](
auto) {
2783 if (
auto result = cast(src, srcType, destType)) {
2784 resultBuffer.push_back(result);
2789 .Default([&](
auto) {
return failure(); });
2792 if (failed(recurse(array, sourceType, destType)))
2795 assert(resultBuffer.size() == 1 &&
2796 "resultBuffer must only contain a result array if `success` is true");
2797 return resultBuffer[0];
2805Value FIRRTLLowering::getLoweredAndExtendedValue(Value src, Type target) {
2806 auto srcType = cast<FIRRTLBaseType>(src.getType());
2807 auto dstType = cast<FIRRTLBaseType>(target);
2808 auto loweredSrc = getLoweredValue(src);
2811 auto dstWidth = dstType.getBitWidthOrSentinel();
2827 return getOrCreateIntConstant(dstWidth, 0);
2830 auto loweredSrcType = loweredSrc.getType();
2831 auto loweredDstType =
lowerType(dstType);
2834 if (loweredSrcType == loweredDstType)
2838 if (dstWidth == srcType.getBitWidthOrSentinel()) {
2840 if (loweredSrcType != loweredDstType &&
2841 (isa<hw::TypeAliasType>(loweredSrcType) ||
2842 isa<hw::TypeAliasType>(loweredDstType))) {
2843 return builder.createOrFold<
hw::BitcastOp>(loweredDstType, loweredSrc);
2848 if (isa<hw::ArrayType, hw::StructType>(loweredSrcType))
2849 return getExtOrTruncAggregateValue(loweredSrc, srcType, dstType,
2852 if (isa<seq::ClockType>(loweredSrcType)) {
2853 builder.emitError(
"cannot use clock type as an integer");
2857 auto intSourceType = dyn_cast<IntegerType>(loweredSrcType);
2858 if (!intSourceType) {
2859 builder.emitError(
"operand of type ")
2860 << loweredSrcType <<
" cannot be used as an integer";
2864 auto loweredSrcWidth = intSourceType.getWidth();
2865 if (loweredSrcWidth ==
unsigned(dstWidth))
2868 if (loweredSrcWidth >
unsigned(dstWidth)) {
2869 builder.emitError(
"operand should not be a truncation");
2874 auto valueFIRType = type_cast<FIRRTLBaseType>(src.getType()).getPassiveType();
2875 if (type_cast<IntType>(valueFIRType).isSigned())
2876 return comb::createOrFoldSExt(builder, loweredSrc, loweredDstType);
2878 auto zero = getOrCreateIntConstant(dstWidth - loweredSrcWidth, 0);
2887Value FIRRTLLowering::getLoweredAndExtOrTruncValue(Value value, Type destType) {
2888 assert(type_isa<FIRRTLBaseType>(value.getType()) &&
2889 type_isa<FIRRTLBaseType>(destType) &&
2890 "input/output value should be FIRRTL");
2893 auto destWidth = type_cast<FIRRTLBaseType>(destType).getBitWidthOrSentinel();
2894 if (destWidth == -1)
2897 auto result = getLoweredValue(value);
2909 return getOrCreateIntConstant(destWidth, 0);
2913 if (isa<hw::ArrayType, hw::StructType>(result.getType())) {
2915 if (destType == value.getType())
2918 return getExtOrTruncAggregateValue(
2919 result, type_cast<FIRRTLBaseType>(value.getType()),
2920 type_cast<FIRRTLBaseType>(destType),
2924 auto srcWidth = type_cast<IntegerType>(result.getType()).getWidth();
2925 if (srcWidth ==
unsigned(destWidth))
2931 if (srcWidth >
unsigned(destWidth)) {
2932 auto resultType = builder.getIntegerType(destWidth);
2936 auto resultType = builder.getIntegerType(destWidth);
2940 type_cast<FIRRTLBaseType>(value.getType()).getPassiveType();
2941 if (type_cast<IntType>(valueFIRType).isSigned())
2942 return comb::createOrFoldSExt(builder, result, resultType);
2944 auto zero = getOrCreateIntConstant(destWidth - srcWidth, 0);
2958std::optional<Value> FIRRTLLowering::getLoweredFmtOperand(Value operand) {
2960 if (type_isa<FStringType>(operand.getType())) {
2961 if (isa<TimeOp>(operand.getDefiningOp()))
2962 return sv::TimeOp::create(builder);
2963 if (isa<HierarchicalModuleNameOp>(operand.getDefiningOp()))
2967 auto loweredValue = getLoweredValue(operand);
2968 if (!loweredValue) {
2972 loweredValue = getOrCreateIntConstant(1, 0);
2977 if (
auto intTy = firrtl::type_cast<IntType>(operand.getType()))
2978 if (intTy.isSigned())
2979 loweredValue = sv::SystemFunctionOp::create(
2980 builder, loweredValue.getType(),
"signed", loweredValue);
2982 return loweredValue;
2986FIRRTLLowering::loweredFmtOperands(mlir::ValueRange operands,
2987 SmallVectorImpl<Value> &loweredOperands) {
2988 for (
auto operand : operands) {
2989 std::optional<Value> loweredValue = getLoweredFmtOperand(operand);
2994 loweredOperands.push_back(*loweredValue);
2999LogicalResult FIRRTLLowering::lowerStatementWithFd(
3000 const FileDescriptorInfo &fileDescriptor, Value clock, Value cond,
3001 const std::function<LogicalResult(Value)> &fn,
bool usePrintfCond) {
3003 bool failed =
false;
3004 circuitState.addMacroDecl(builder.getStringAttr(
"SYNTHESIS"));
3005 addToIfDefBlock(
"SYNTHESIS", std::function<
void()>(), [&]() {
3006 addToAlwaysBlock(clock, [&]() {
3009 circuitState.usedPrintf =
true;
3011 circuitState.addFragment(theModule,
"PRINTF_COND_FRAGMENT");
3014 Value ifCond = cond;
3015 if (usePrintfCond) {
3017 sv::MacroRefExprOp::create(builder, cond.getType(),
"PRINTF_COND_");
3018 ifCond = builder.createOrFold<
comb::AndOp>(ifCond, cond,
true);
3021 addIfProceduralBlock(ifCond, [&]() {
3025 if (fileDescriptor.isDefaultFd()) {
3027 fd = hw::ConstantOp::create(builder, APInt(32, 0x80000002));
3030 auto fdOrError = callFileDescriptorLib(fileDescriptor);
3031 if (llvm::failed(fdOrError)) {
3037 failed = llvm::failed(fn(fd));
3041 return failure(failed);
3045FIRRTLLowering::callFileDescriptorLib(
const FileDescriptorInfo &info) {
3046 circuitState.usedFileDescriptorLib =
true;
3047 circuitState.addFragment(theModule,
"CIRCT_LIB_LOGGING_FRAGMENT");
3050 if (
info.isSubstitutionRequired()) {
3051 SmallVector<Value> fileNameOperands;
3052 if (failed(loweredFmtOperands(
info.getSubstitutions(), fileNameOperands)))
3055 fileName = sv::SFormatFOp::create(builder,
info.getOutputFileFormat(),
3060 fileName = sv::ConstantStrOp::create(builder,
info.getOutputFileFormat())
3064 return sv::FuncCallProceduralOp::create(
3065 builder, mlir::TypeRange{builder.getIntegerType(32)},
3066 builder.getStringAttr(
"__circt_lib_logging::FileDescriptor::get"),
3067 ValueRange{fileName})
3077LogicalResult FIRRTLLowering::setLowering(Value orig, Value result) {
3078 if (
auto origType = dyn_cast<FIRRTLType>(orig.getType())) {
3079 assert((!result || !type_isa<FIRRTLType>(result.getType())) &&
3080 "Lowering didn't turn a FIRRTL value into a non-FIRRTL value");
3084 auto srcWidth = baseType.getPassiveType().getBitWidthOrSentinel();
3087 if (srcWidth != -1) {
3089 assert((srcWidth != 0) &&
3090 "Lowering produced value for zero width source");
3092 assert((srcWidth == 0) &&
3093 "Lowering produced null value but source wasn't zero width");
3097 assert(result &&
"Lowering of foreign type produced null value");
3100 auto &slot = valueMapping[orig];
3101 assert(!slot &&
"value lowered multiple times");
3108LogicalResult FIRRTLLowering::setPossiblyFoldedLowering(Value orig,
3112 if (
auto cst = dyn_cast_or_null<hw::ConstantOp>(result.getDefiningOp())) {
3113 auto &entry = hwConstantMap[cst.getValueAttr()];
3124 cst->moveBefore(&theModule.getBodyBlock()->front());
3128 return setLowering(orig, result);
3133template <
typename ResultOpType,
typename... CtorArgTypes>
3134LogicalResult FIRRTLLowering::setLoweringTo(Operation *orig,
3135 CtorArgTypes... args) {
3136 auto result = builder.createOrFold<ResultOpType>(args...);
3137 if (
auto *op = result.getDefiningOp())
3139 return setPossiblyFoldedLowering(orig->getResult(0), result);
3146template <
typename ResultOpType,
typename... CtorArgTypes>
3147LogicalResult FIRRTLLowering::setLoweringToLTL(Operation *orig,
3148 CtorArgTypes... args) {
3149 auto result = builder.createOrFold<ResultOpType>(args...);
3150 if (
auto *op = result.getDefiningOp())
3151 ltlOpFixupWorklist.insert(op);
3152 return setPossiblyFoldedLowering(orig->getResult(0), result);
3161Backedge FIRRTLLowering::createBackedge(Location loc, Type type) {
3162 auto backedge = backedgeBuilder.
get(type, loc);
3163 backedges.insert({backedge, backedge});
3171Backedge FIRRTLLowering::createBackedge(Value orig, Type type) {
3172 auto backedge = createBackedge(orig.getLoc(), type);
3173 (void)setLowering(orig, backedge);
3179bool FIRRTLLowering::updateIfBackedge(Value dest, Value src) {
3180 auto backedgeIt = backedges.find(dest);
3181 if (backedgeIt == backedges.end())
3183 backedgeIt->second = src;
3191void FIRRTLLowering::runWithInsertionPointAtEndOfBlock(
3192 const std::function<
void(
void)> &fn, Region ®ion) {
3196 auto oldIP = builder.saveInsertionPoint();
3198 builder.setInsertionPointToEnd(®ion.front());
3200 builder.restoreInsertionPoint(oldIP);
3204Value FIRRTLLowering::getReadValue(Value v) {
3205 Value result = readInOutCreated.lookup(v);
3211 auto oldIP = builder.saveInsertionPoint();
3212 if (
auto *vOp = v.getDefiningOp()) {
3213 builder.setInsertionPointAfter(vOp);
3217 builder.setInsertionPoint(&theModule.getBodyBlock()->front());
3222 if (
auto arrayIndexInout = v.getDefiningOp<sv::ArrayIndexInOutOp>()) {
3223 result = getReadValue(arrayIndexInout.getInput());
3225 arrayIndexInout.getIndex());
3230 builder.restoreInsertionPoint(oldIP);
3231 readInOutCreated.insert({v, result});
3235Value FIRRTLLowering::getNonClockValue(Value v) {
3236 auto it = fromClockMapping.try_emplace(v, Value{});
3238 ImplicitLocOpBuilder builder(v.getLoc(), v.getContext());
3239 builder.setInsertionPointAfterValue(v);
3240 it.first->second = seq::FromClockOp::create(builder, v);
3242 return it.first->second;
3245void FIRRTLLowering::addToAlwaysBlock(
3246 sv::EventControl clockEdge, Value clock, sv::ResetType resetStyle,
3247 sv::EventControl resetEdge, Value reset,
3248 const std::function<
void(
void)> &body,
3249 const std::function<
void(
void)> &resetBody) {
3250 AlwaysKeyType key{builder.getBlock(), clockEdge, clock,
3251 resetStyle, resetEdge, reset};
3252 sv::AlwaysOp alwaysOp;
3253 sv::IfOp insideIfOp;
3254 std::tie(alwaysOp, insideIfOp) = alwaysBlocks.lookup(key);
3258 assert(resetStyle != sv::ResetType::NoReset);
3271 auto createIfOp = [&]() {
3274 insideIfOp = sv::IfOp::create(
3275 builder, reset, [] {}, [] {});
3277 if (resetStyle == sv::ResetType::AsyncReset) {
3278 sv::EventControl events[] = {clockEdge, resetEdge};
3279 Value clocks[] = {clock, reset};
3281 alwaysOp = sv::AlwaysOp::create(builder, events, clocks, [&]() {
3282 if (resetEdge == sv::EventControl::AtNegEdge)
3283 llvm_unreachable(
"negative edge for reset is not expected");
3287 alwaysOp = sv::AlwaysOp::create(builder, clockEdge, clock, createIfOp);
3291 alwaysOp = sv::AlwaysOp::create(builder, clockEdge, clock);
3292 insideIfOp =
nullptr;
3294 alwaysBlocks[key] = {alwaysOp, insideIfOp};
3298 assert(insideIfOp &&
"reset body must be initialized before");
3299 runWithInsertionPointAtEndOfBlock(resetBody, insideIfOp.getThenRegion());
3300 runWithInsertionPointAtEndOfBlock(body, insideIfOp.getElseRegion());
3302 runWithInsertionPointAtEndOfBlock(body, alwaysOp.getBody());
3308 alwaysOp->moveBefore(builder.getInsertionBlock(),
3309 builder.getInsertionPoint());
3312LogicalResult FIRRTLLowering::emitGuards(Location loc,
3313 ArrayRef<Attribute> guards,
3314 std::function<
void(
void)>
emit) {
3315 if (guards.empty()) {
3319 auto guard = dyn_cast<StringAttr>(guards[0]);
3321 return mlir::emitError(loc,
3322 "elements in `guards` array must be `StringAttr`");
3325 circuitState.addMacroDecl(builder.getStringAttr(guard.getValue()));
3326 LogicalResult result = LogicalResult::failure();
3327 addToIfDefBlock(guard.getValue(), [&]() {
3328 result = emitGuards(loc, guards.drop_front(), emit);
3333void FIRRTLLowering::addToIfDefBlock(StringRef cond,
3334 std::function<
void(
void)> thenCtor,
3335 std::function<
void(
void)> elseCtor) {
3336 auto condAttr = builder.getStringAttr(cond);
3337 auto op = ifdefBlocks.lookup({builder.getBlock(), condAttr});
3339 runWithInsertionPointAtEndOfBlock(thenCtor, op.getThenRegion());
3340 runWithInsertionPointAtEndOfBlock(elseCtor, op.getElseRegion());
3345 op->moveBefore(builder.getInsertionBlock(), builder.getInsertionPoint());
3347 ifdefBlocks[{builder.getBlock(), condAttr}] =
3348 sv::IfDefOp::create(builder, condAttr, thenCtor, elseCtor);
3352void FIRRTLLowering::addToInitialBlock(std::function<
void(
void)> body) {
3353 auto op = initialBlocks.lookup(builder.getBlock());
3355 runWithInsertionPointAtEndOfBlock(body, op.getBody());
3360 op->moveBefore(builder.getInsertionBlock(), builder.getInsertionPoint());
3362 initialBlocks[builder.getBlock()] = sv::InitialOp::create(builder, body);
3366void FIRRTLLowering::addIfProceduralBlock(Value cond,
3367 std::function<
void(
void)> thenCtor,
3368 std::function<
void(
void)> elseCtor) {
3371 auto insertIt = builder.getInsertionPoint();
3372 if (insertIt != builder.getBlock()->begin())
3373 if (
auto ifOp = dyn_cast<sv::IfOp>(*--insertIt)) {
3374 if (ifOp.getCond() == cond) {
3375 runWithInsertionPointAtEndOfBlock(thenCtor, ifOp.getThenRegion());
3376 runWithInsertionPointAtEndOfBlock(elseCtor, ifOp.getElseRegion());
3381 sv::IfOp::create(builder, cond, thenCtor, elseCtor);
3393FIRRTLLowering::UnloweredOpResult
3394FIRRTLLowering::handleUnloweredOp(Operation *op) {
3396 if (!op->getRegions().empty() &&
3397 isa_and_nonnull<FIRRTLDialect>(op->getDialect())) {
3398 op->emitOpError(
"must explicitly handle its regions");
3399 return LoweringFailure;
3406 if (!isa_and_nonnull<FIRRTLDialect>(op->getDialect())) {
3408 for (
auto ®ion : op->getRegions())
3409 addToWorklist(region);
3410 for (
auto &operand : op->getOpOperands())
3411 if (auto lowered = getPossiblyInoutLoweredValue(operand.
get()))
3412 operand.set(lowered);
3413 for (
auto result : op->getResults())
3414 (void)setLowering(result, result);
3415 return AlreadyLowered;
3427 if (op->getNumResults() == 1) {
3428 auto resultType = op->getResult(0).getType();
3429 if (type_isa<FIRRTLBaseType>(resultType) &&
3431 (
isExpression(op) || isa<mlir::UnrealizedConversionCastOp>(op))) {
3433 (void)setLowering(op->getResult(0), Value());
3437 op->emitOpError(
"LowerToHW couldn't handle this operation");
3438 return LoweringFailure;
3441LogicalResult FIRRTLLowering::visitExpr(ConstantOp op) {
3444 return setLowering(op, Value());
3446 return setLowering(op, getOrCreateIntConstant(op.getValue()));
3449LogicalResult FIRRTLLowering::visitExpr(SpecialConstantOp op) {
3451 if (isa<ClockType>(op.getType())) {
3452 cst = getOrCreateClockConstant(op.getValue() ? seq::ClockConst::High
3453 :
seq::ClockConst::Low);
3455 cst = getOrCreateIntConstant(APInt( 1, op.getValue()));
3457 return setLowering(op, cst);
3460FailureOr<Value> FIRRTLLowering::lowerSubindex(SubindexOp op, Value input) {
3461 auto iIdx = getOrCreateIntConstant(
3463 firrtl::type_cast<FVectorType>(op.getInput().getType())
3470 if (isa<sv::InOutType>(input.getType()))
3471 result = builder.createOrFold<sv::ArrayIndexInOutOp>(input, iIdx);
3474 if (
auto *definingOp = result.getDefiningOp())
3479FailureOr<Value> FIRRTLLowering::lowerSubaccess(SubaccessOp op, Value input) {
3480 Value valueIdx = getLoweredAndExtOrTruncValue(
3482 UIntType::get(op->getContext(),
3484 firrtl::type_cast<FVectorType>(op.getInput().getType())
3485 .getNumElements())));
3487 op->emitError() <<
"input lowering failed";
3494 if (isa<sv::InOutType>(input.getType()))
3495 result = builder.createOrFold<sv::ArrayIndexInOutOp>(input, valueIdx);
3497 result = createArrayIndexing(input, valueIdx);
3498 if (
auto *definingOp = result.getDefiningOp())
3503FailureOr<Value> FIRRTLLowering::lowerSubfield(SubfieldOp op, Value input) {
3504 auto resultType =
lowerType(op->getResult(0).getType());
3505 if (!resultType || !input) {
3506 op->emitError() <<
"subfield type lowering failed";
3512 auto field = firrtl::type_cast<BundleType>(op.getInput().getType())
3513 .getElementName(op.getFieldIndex());
3515 if (isa<sv::InOutType>(input.getType()))
3516 result = builder.createOrFold<sv::StructFieldInOutOp>(input, field);
3519 if (
auto *definingOp = result.getDefiningOp())
3524LogicalResult FIRRTLLowering::visitExpr(SubindexOp op) {
3526 return setLowering(op, Value());
3528 auto input = getPossiblyInoutLoweredValue(op.getInput());
3530 return op.emitError() <<
"input lowering failed";
3532 auto result = lowerSubindex(op, input);
3535 return setLowering(op, *result);
3538LogicalResult FIRRTLLowering::visitExpr(SubaccessOp op) {
3540 return setLowering(op, Value());
3542 auto input = getPossiblyInoutLoweredValue(op.getInput());
3544 return op.emitError() <<
"input lowering failed";
3546 auto result = lowerSubaccess(op, input);
3549 return setLowering(op, *result);
3552LogicalResult FIRRTLLowering::visitExpr(SubfieldOp op) {
3555 if (getLoweredValue(op) || !op.getInput())
3559 return setLowering(op, Value());
3561 auto input = getPossiblyInoutLoweredValue(op.getInput());
3563 return op.emitError() <<
"input lowering failed";
3565 auto result = lowerSubfield(op, input);
3568 return setLowering(op, *result);
3571LogicalResult FIRRTLLowering::visitExpr(VectorCreateOp op) {
3572 auto resultType =
lowerType(op.getResult().getType());
3573 SmallVector<Value> operands;
3575 for (
auto oper :
llvm::reverse(op.getOperands())) {
3576 auto val = getLoweredValue(oper);
3579 operands.push_back(val);
3581 return setLoweringTo<hw::ArrayCreateOp>(op, resultType, operands);
3584LogicalResult FIRRTLLowering::visitExpr(BundleCreateOp op) {
3585 auto resultType =
lowerType(op.getResult().getType());
3586 SmallVector<Value> operands;
3587 for (
auto oper : op.getOperands()) {
3588 auto val = getLoweredValue(oper);
3591 operands.push_back(val);
3593 return setLoweringTo<hw::StructCreateOp>(op, resultType, operands);
3596LogicalResult FIRRTLLowering::visitExpr(FEnumCreateOp op) {
3599 return setLowering(op, Value());
3601 auto input = getLoweredValue(op.getInput());
3602 auto tagName = op.getFieldNameAttr();
3603 auto oldType = op.getType().base();
3605 auto element = *oldType.getElement(op.getFieldNameAttr());
3607 if (
auto structType = dyn_cast<hw::StructType>(newType)) {
3608 auto tagType = structType.getFieldType(
"tag");
3609 auto tagValue = IntegerAttr::get(tagType, element.value.getValue());
3610 auto tag = sv::LocalParamOp::create(builder, op.getLoc(), tagType, tagValue,
3612 auto bodyType = structType.getFieldType(
"body");
3613 auto body = hw::UnionCreateOp::create(builder, bodyType, tagName, input);
3614 SmallVector<Value> operands = {tag.getResult(), body.getResult()};
3615 return setLoweringTo<hw::StructCreateOp>(op, structType, operands);
3617 auto tagValue = IntegerAttr::get(newType, element.value.getValue());
3618 return setLoweringTo<sv::LocalParamOp>(op, newType, tagValue, tagName);
3621LogicalResult FIRRTLLowering::visitExpr(AggregateConstantOp op) {
3622 auto resultType =
lowerType(op.getResult().getType());
3624 getOrCreateAggregateConstantAttribute(op.getFieldsAttr(), resultType);
3626 return setLoweringTo<hw::AggregateConstantOp>(op, resultType,
3627 cast<ArrayAttr>(attr));
3630LogicalResult FIRRTLLowering::visitExpr(IsTagOp op) {
3632 auto tagName = op.getFieldNameAttr();
3633 auto lhs = getLoweredValue(op.getInput());
3634 if (isa<hw::StructType>(lhs.getType()))
3637 auto index = op.getFieldIndex();
3638 auto enumType = op.getInput().getType().base();
3639 auto tagValue = enumType.getElementValueAttr(index);
3640 auto tagValueType = IntegerType::get(op.getContext(), enumType.getTagWidth());
3641 auto loweredTagValue = IntegerAttr::get(tagValueType, tagValue.getValue());
3642 auto rhs = sv::LocalParamOp::create(builder, op.getLoc(), tagValueType,
3643 loweredTagValue, tagName);
3645 Type resultType = builder.getIntegerType(1);
3646 return setLoweringTo<comb::ICmpOp>(op, resultType, ICmpPredicate::eq, lhs,
3650LogicalResult FIRRTLLowering::visitExpr(SubtagOp op) {
3653 return setLowering(op, Value());
3655 auto tagName = op.getFieldNameAttr();
3656 auto input = getLoweredValue(op.getInput());
3658 return setLoweringTo<hw::UnionExtractOp>(op, field, tagName);
3661LogicalResult FIRRTLLowering::visitExpr(TagExtractOp op) {
3664 return setLowering(op, Value());
3666 auto input = getLoweredValue(op.getInput());
3672 if (isa<hw::StructType>(input.getType())) {
3673 return setLoweringTo<hw::StructExtractOp>(op, input,
"tag");
3678 return setLowering(op, input);
3685LogicalResult FIRRTLLowering::visitDecl(WireOp op) {
3686 auto origResultType = op.getResult().getType();
3690 if (!type_isa<FIRRTLType>(origResultType)) {
3691 createBackedge(op.getResult(), origResultType);
3695 auto resultType =
lowerType(origResultType);
3699 if (resultType.isInteger(0)) {
3700 if (op.getInnerSym())
3701 return op.emitError(
"zero width wire is referenced by name [")
3702 << *op.getInnerSym() <<
"] (e.g. in an XMR) but must be removed";
3703 return setLowering(op.getResult(), Value());
3707 auto innerSym = lowerInnerSymbol(op);
3708 auto name = op.getNameAttr();
3711 auto wire = hw::WireOp::create(
3712 builder, op.getLoc(), getOrCreateZConstant(resultType), name, innerSym);
3714 if (
auto svAttrs = sv::getSVAttributes(op))
3715 sv::setSVAttributes(wire, svAttrs);
3717 return setLowering(op.getResult(), wire);
3720LogicalResult FIRRTLLowering::visitDecl(VerbatimWireOp op) {
3721 auto resultTy =
lowerType(op.getType());
3724 resultTy = sv::InOutType::get(op.getContext(), resultTy);
3726 SmallVector<Value, 4> operands;
3727 operands.reserve(op.getSubstitutions().size());
3728 for (
auto operand : op.getSubstitutions()) {
3729 auto lowered = getLoweredValue(operand);
3732 operands.push_back(lowered);
3735 ArrayAttr symbols = op.getSymbolsAttr();
3737 symbols = ArrayAttr::get(op.getContext(), {});
3739 return setLoweringTo<sv::VerbatimExprSEOp>(op, resultTy, op.getTextAttr(),
3743LogicalResult FIRRTLLowering::visitDecl(NodeOp op) {
3744 auto operand = getLoweredValue(op.getInput());
3746 return handleZeroBit(op.getInput(), [&]() -> LogicalResult {
3747 if (op.getInnerSym())
3748 return op.emitError(
"zero width node is referenced by name [")
3749 << *op.getInnerSym()
3750 <<
"] (e.g. in an XMR) but must be "
3752 return setLowering(op.getResult(), Value());
3758 auto name = op.getNameAttr();
3759 auto innerSym = lowerInnerSymbol(op);
3762 operand = hw::WireOp::create(builder, operand, name, innerSym);
3765 if (
auto svAttrs = sv::getSVAttributes(op)) {
3767 operand = hw::WireOp::create(builder, operand, name);
3768 sv::setSVAttributes(operand.getDefiningOp(), svAttrs);
3771 return setLowering(op.getResult(), operand);
3774LogicalResult FIRRTLLowering::visitDecl(RegOp op) {
3775 auto resultType =
lowerType(op.getResult().getType());
3778 if (resultType.isInteger(0))
3779 return setLowering(op.getResult(), Value());
3781 Value clockVal = getLoweredValue(op.getClockVal());
3786 auto innerSym = lowerInnerSymbol(op);
3787 Backedge inputEdge = backedgeBuilder.
get(resultType);
3788 auto reg = seq::FirRegOp::create(builder, inputEdge, clockVal,
3789 op.getNameAttr(), innerSym);
3792 if (
auto randomRegister = op->getAttr(
"firrtl.random_init_register"))
3793 reg->setAttr(
"firrtl.random_init_register", randomRegister);
3794 if (
auto randomStart = op->getAttr(
"firrtl.random_init_start"))
3795 reg->setAttr(
"firrtl.random_init_start", randomStart);
3796 if (
auto randomEnd = op->getAttr(
"firrtl.random_init_end"))
3797 reg->setAttr(
"firrtl.random_init_end", randomEnd);
3800 if (
auto svAttrs = sv::getSVAttributes(op))
3801 sv::setSVAttributes(reg, svAttrs);
3804 (void)setLowering(op.getResult(),
reg);
3808LogicalResult FIRRTLLowering::visitDecl(RegResetOp op) {
3809 auto resultType =
lowerType(op.getResult().getType());
3812 if (resultType.isInteger(0))
3813 return setLowering(op.getResult(), Value());
3815 Value clockVal = getLoweredValue(op.getClockVal());
3816 Value resetSignal = getLoweredValue(op.getResetSignal());
3818 Value resetValue = getLoweredAndExtOrTruncValue(
3819 op.getResetValue(), type_cast<FIRRTLBaseType>(op.getResult().getType()));
3821 if (!clockVal || !resetSignal || !resetValue)
3825 auto innerSym = lowerInnerSymbol(op);
3826 bool isAsync = type_isa<AsyncResetType>(op.getResetSignal().getType());
3827 Backedge inputEdge = backedgeBuilder.
get(resultType);
3829 seq::FirRegOp::create(builder, inputEdge, clockVal, op.getNameAttr(),
3830 resetSignal, resetValue, innerSym, isAsync);
3833 if (
auto randomRegister = op->getAttr(
"firrtl.random_init_register"))
3834 reg->setAttr(
"firrtl.random_init_register", randomRegister);
3835 if (
auto randomStart = op->getAttr(
"firrtl.random_init_start"))
3836 reg->setAttr(
"firrtl.random_init_start", randomStart);
3837 if (
auto randomEnd = op->getAttr(
"firrtl.random_init_end"))
3838 reg->setAttr(
"firrtl.random_init_end", randomEnd);
3841 if (
auto svAttrs = sv::getSVAttributes(op))
3842 sv::setSVAttributes(reg, svAttrs);
3845 (void)setLowering(op.getResult(),
reg);
3850LogicalResult FIRRTLLowering::visitDecl(MemOp op) {
3853 if (type_isa<BundleType>(op.getDataType()))
3854 return op.emitOpError(
3855 "should have already been lowered from a ground type to an aggregate "
3856 "type using the LowerTypes pass. Use "
3857 "'firtool --lower-types' or 'circt-opt "
3858 "--pass-pipeline='firrtl.circuit(firrtl-lower-types)' "
3864 auto memType = seq::FirMemType::get(
3867 : std::optional<uint32_t>());
3869 seq::FirMemInitAttr memInit;
3870 if (
auto init = op.getInitAttr())
3871 memInit = seq::FirMemInitAttr::get(init.getContext(), init.getFilename(),
3872 init.getIsBinary(), init.getIsInline());
3874 auto memDecl = seq::FirMemOp::create(
3877 op.getInnerSymAttr(), memInit, op.getPrefixAttr(), Attribute{});
3880 if (
auto file = parent->getAttrOfType<hw::OutputFileAttr>(
"output_file")) {
3882 if (!file.isDirectory())
3883 dir = hw::OutputFileAttr::getAsDirectory(builder.getContext(),
3884 file.getDirectory());
3885 memDecl.setOutputFileAttr(dir);
3891 for (
size_t i = 0, e = op.getNumResults(); i != e; ++i) {
3893 auto addOutput = [&](StringRef field,
size_t width, Value value) {
3896 (void)setLowering(a, value);
3902 auto addInput = [&](StringRef field, Value backedge) {
3904 if (cast<FIRRTLBaseType>(
a.getType())
3906 .getBitWidthOrSentinel() > 0)
3907 (void)setLowering(a, backedge);
3913 auto addInputPort = [&](StringRef field,
size_t width) -> Value {
3917 Value backedge, portValue;
3919 portValue = getOrCreateXConstant(1);
3921 auto portType = IntegerType::get(op.getContext(), width);
3922 backedge = portValue = createBackedge(builder.getLoc(), portType);
3924 addInput(field, backedge);
3928 auto addClock = [&](StringRef field) -> Value {
3929 Type clockTy = seq::ClockType::get(op.getContext());
3930 Value portValue = createBackedge(builder.getLoc(), clockTy);
3931 addInput(field, portValue);
3935 auto memportKind = op.getPortKind(i);
3936 if (memportKind == MemOp::PortKind::Read) {
3937 auto addr = addInputPort(
"addr", op.getAddrBits());
3938 auto en = addInputPort(
"en", 1);
3939 auto clk = addClock(
"clk");
3940 auto data = seq::FirMemReadOp::create(builder, memDecl,
addr,
clk,
en);
3942 }
else if (memportKind == MemOp::PortKind::ReadWrite) {
3943 auto addr = addInputPort(
"addr", op.getAddrBits());
3944 auto en = addInputPort(
"en", 1);
3945 auto clk = addClock(
"clk");
3948 auto mode = addInputPort(
"wmode", 1);
3950 mode = builder.createOrFold<
comb::AndOp>(mode, addInputPort(
"wmask", 1),
3957 auto rdata = seq::FirMemReadWriteOp::create(builder, memDecl,
addr,
clk,
3961 auto addr = addInputPort(
"addr", op.getAddrBits());
3964 auto en = addInputPort(
"en", 1);
3968 auto clk = addClock(
"clk");
3982FIRRTLLowering::prepareInstanceOperands(ArrayRef<PortInfo> portInfo,
3983 Operation *instanceOp,
3984 SmallVectorImpl<Value> &inputOperands) {
3986 for (
size_t portIndex = 0, e = portInfo.size(); portIndex != e; ++portIndex) {
3987 auto &port = portInfo[portIndex];
3990 instanceOp->emitOpError(
"could not lower type of port ") << port.name;
3995 if (portType.isInteger(0))
3999 if (port.isOutput())
4002 auto portResult = instanceOp->getResult(portIndex);
4003 assert(portResult &&
"invalid IR, couldn't find port");
4007 if (port.isInput()) {
4008 inputOperands.push_back(createBackedge(portResult, portType));
4014 if (type_isa<AnalogType>(portResult.getType()) && portResult.hasOneUse()) {
4015 if (
auto attach = dyn_cast<AttachOp>(*portResult.getUsers().begin())) {
4017 auto loweredResult = getPossiblyInoutLoweredValue(source);
4018 inputOperands.push_back(loweredResult);
4019 (void)setLowering(portResult, loweredResult);
4028 "." + port.getName().str() +
".wire");
4032 (void)setLowering(portResult, wire);
4033 inputOperands.push_back(wire);
4039LogicalResult FIRRTLLowering::visitDecl(InstanceOp oldInstance) {
4040 Operation *oldModule =
4041 oldInstance.getReferencedModule(circuitState.getInstanceGraph());
4043 auto *newModule = circuitState.getNewModule(oldModule);
4045 oldInstance->emitOpError(
"could not find module [")
4046 << oldInstance.getModuleName() <<
"] referenced by instance";
4052 ArrayAttr parameters;
4053 if (
auto oldExtModule = dyn_cast<FExtModuleOp>(oldModule))
4058 SmallVector<PortInfo, 8> portInfo = cast<FModuleLike>(oldModule).getPorts();
4062 SmallVector<Value, 8> operands;
4063 if (failed(prepareInstanceOperands(portInfo, oldInstance, operands)))
4070 auto innerSym = oldInstance.getInnerSymAttr();
4071 if (oldInstance.getLowerToBind()) {
4074 oldInstance.getContext(), oldInstance.getInnerSymAttr(), 0,
4077 auto bindOp = sv::BindOp::create(builder, theModule.getNameAttr(),
4078 innerSym.getSymName());
4081 if (
auto outputFile = oldInstance->getAttr(
"output_file"))
4082 bindOp->setAttr(
"output_file", outputFile);
4085 circuitState.addBind(bindOp);
4090 hw::InstanceOp::create(builder, newModule, oldInstance.getNameAttr(),
4091 operands, parameters, innerSym);
4093 if (oldInstance.getLowerToBind() || oldInstance.getDoNotPrint())
4094 newInstance.setDoNotPrintAttr(builder.getUnitAttr());
4096 if (newInstance.getInnerSymAttr())
4097 if (
auto forceName = circuitState.instanceForceNames.lookup(
4098 {newInstance->getParentOfType<hw::HWModuleOp>().getNameAttr(),
4099 newInstance.getInnerNameAttr()}))
4100 newInstance->setAttr(
"hw.verilogName", forceName);
4104 unsigned resultNo = 0;
4105 for (
size_t portIndex = 0, e = portInfo.size(); portIndex != e; ++portIndex) {
4106 auto &port = portInfo[portIndex];
4110 Value resultVal = newInstance.getResult(resultNo);
4112 auto oldPortResult = oldInstance.getResult(portIndex);
4113 (void)setLowering(oldPortResult, resultVal);
4119LogicalResult FIRRTLLowering::visitDecl(InstanceChoiceOp oldInstanceChoice) {
4120 if (oldInstanceChoice.getInnerSymAttr()) {
4121 oldInstanceChoice->emitOpError(
4122 "instance choice with inner sym cannot be lowered");
4127 FlatSymbolRefAttr instanceMacro = oldInstanceChoice.getInstanceMacroAttr();
4129 return oldInstanceChoice->emitOpError(
4130 "must have instance_macro attribute set before "
4134 auto moduleNames = oldInstanceChoice.getModuleNamesAttr();
4135 auto caseNames = oldInstanceChoice.getCaseNamesAttr();
4138 auto defaultModuleName = oldInstanceChoice.getDefaultTargetAttr();
4139 auto *defaultModuleNode =
4140 circuitState.getInstanceGraph().lookup(defaultModuleName.getAttr());
4142 Operation *defaultModule = defaultModuleNode->getModule();
4146 SmallVector<PortInfo, 8> portInfo =
4147 cast<FModuleLike>(defaultModule).getPorts();
4150 SmallVector<Value, 8> inputOperands;
4152 prepareInstanceOperands(portInfo, oldInstanceChoice, inputOperands)))
4156 SmallVector<sv::WireOp, 8> outputWires;
4157 StringRef wirePrefix = oldInstanceChoice.getInstanceName();
4158 for (
size_t portIndex = 0, e = portInfo.size(); portIndex != e; ++portIndex) {
4159 auto &port = portInfo[portIndex];
4163 if (!portType || portType.isInteger(0))
4166 builder, portType, wirePrefix.str() +
"." + port.getName().str());
4167 outputWires.push_back(wire);
4168 if (failed(setLowering(oldInstanceChoice.getResult(portIndex), wire)))
4172 auto optionName = oldInstanceChoice.getOptionNameAttr();
4175 auto createInstanceAndAssign = [&](Operation *oldMod,
4176 StringRef suffix) -> hw::InstanceOp {
4177 auto *newMod = circuitState.getNewModule(oldMod);
4179 ArrayAttr parameters;
4180 if (
auto oldExtModule = dyn_cast<FExtModuleOp>(oldMod))
4184 SmallString<64> instName;
4185 instName = oldInstanceChoice.getInstanceName();
4186 if (!suffix.empty()) {
4192 hw::InstanceOp::create(builder, newMod, builder.getStringAttr(instName),
4193 inputOperands, parameters,
nullptr);
4199 for (
unsigned i = 0; i < inst.getNumResults(); ++i)
4206 SmallVector<StringAttr> macroNames;
4207 SmallVector<Operation *> altModules;
4208 for (
size_t i = 0, e = caseNames.size(); i < e; ++i) {
4209 altModules.push_back(
4210 circuitState.getInstanceGraph()
4211 .lookup(cast<FlatSymbolRefAttr>(moduleNames[i + 1]).getAttr())
4215 auto optionCaseMacroRef = circuitState.macroTable.getMacro(
4216 optionName, cast<SymbolRefAttr>(caseNames[i]).getLeafReference());
4217 if (!optionCaseMacroRef)
4218 return oldInstanceChoice->emitOpError(
4219 "failed to get macro for option case");
4220 macroNames.push_back(optionCaseMacroRef.getAttr());
4224 sv::createNestedIfDefs(
4227 [&](StringRef macro, std::function<
void()> thenCtor,
4228 std::function<
void()> elseCtor) {
4229 addToIfDefBlock(macro, std::move(thenCtor), std::move(elseCtor));
4233 cast<SymbolRefAttr>(caseNames[index]).getLeafReference();
4234 circuitState.addInstanceChoiceForCase(
4235 optionName, caseSymRef, theModule.getNameAttr(), instanceMacro,
4236 createInstanceAndAssign(altModules[index], caseSymRef.getValue()));
4239 auto inst = createInstanceAndAssign(defaultModule,
"default");
4241 sv::IfDefOp::create(
4242 builder, inst.getLoc(), instanceMacro, [&]() {},
4244 sv::MacroDefOp::create(
4245 builder, inst.getLoc(), instanceMacro,
4246 builder.getStringAttr(
"{{0}}"),
4247 builder.getArrayAttr({hw::InnerRefAttr::get(
4248 theModule.getNameAttr(),
4249 inst.getInnerSymAttr().getSymName())}));
4256LogicalResult FIRRTLLowering::visitDecl(ContractOp oldOp) {
4257 SmallVector<Value> inputs;
4258 SmallVector<Type> types;
4259 for (
auto input : oldOp.getInputs()) {
4260 auto lowered = getLoweredValue(input);
4263 inputs.push_back(lowered);
4264 types.push_back(lowered.getType());
4267 auto newOp = verif::ContractOp::create(builder, types, inputs);
4268 newOp->setDiscardableAttrs(oldOp->getDiscardableAttrDictionary());
4269 auto &body = newOp.getBody().emplaceBlock();
4271 for (
auto [newResult, oldResult, oldArg] :
4272 llvm::zip(newOp.getResults(), oldOp.getResults(),
4273 oldOp.getBody().getArguments())) {
4274 if (failed(setLowering(oldResult, newResult)))
4276 if (failed(setLowering(oldArg, newResult)))
4280 body.getOperations().splice(body.end(),
4281 oldOp.getBody().front().getOperations());
4282 addToWorklist(body);
4292LogicalResult FIRRTLLowering::lowerNoopCast(Operation *op) {
4293 auto operand = getPossiblyInoutLoweredValue(op->getOperand(0));
4298 return setLowering(op->getResult(0), operand);
4301LogicalResult FIRRTLLowering::visitExpr(AsSIntPrimOp op) {
4302 if (isa<ClockType>(op.getInput().getType()))
4303 return setLowering(op->getResult(0),
4304 getLoweredNonClockValue(op.getInput()));
4305 return lowerNoopCast(op);
4308LogicalResult FIRRTLLowering::visitExpr(AsUIntPrimOp op) {
4309 if (isa<ClockType>(op.getInput().getType()))
4310 return setLowering(op->getResult(0),
4311 getLoweredNonClockValue(op.getInput()));
4312 return lowerNoopCast(op);
4315LogicalResult FIRRTLLowering::visitExpr(AsClockPrimOp op) {
4316 return setLoweringTo<seq::ToClockOp>(op, getLoweredValue(op.getInput()));
4319LogicalResult FIRRTLLowering::visitUnrealizedConversionCast(
4320 mlir::UnrealizedConversionCastOp op) {
4322 if (op.getNumOperands() != 1 || op.getNumResults() != 1)
4325 auto operand = op.getOperand(0);
4326 auto result = op.getResult(0);
4329 if (type_isa<FIRRTLType>(operand.getType()) &&
4330 type_isa<FIRRTLType>(result.getType()))
4331 return lowerNoopCast(op);
4335 if (!type_isa<FIRRTLType>(operand.getType())) {
4336 if (type_isa<FIRRTLType>(result.getType()))
4337 return setLowering(result, getPossiblyInoutLoweredValue(operand));
4343 auto loweredResult = getLoweredValue(operand);
4344 if (!loweredResult) {
4347 if (operand.getType().isSignlessInteger(0)) {
4348 return setLowering(result, Value());
4355 result.replaceAllUsesWith(loweredResult);
4359LogicalResult FIRRTLLowering::visitExpr(HWStructCastOp op) {
4362 if (
auto opStructType = dyn_cast<hw::StructType>(op.getOperand().getType()))
4363 return setLowering(op, op.getOperand());
4367 auto result = getLoweredValue(op.getOperand());
4373 op.replaceAllUsesWith(result);
4377LogicalResult FIRRTLLowering::visitExpr(BitCastOp op) {
4378 auto operand = getLoweredValue(op.getOperand());
4381 auto resultType =
lowerType(op.getType());
4385 return setLoweringTo<hw::BitcastOp>(op, resultType, operand);
4388LogicalResult FIRRTLLowering::visitExpr(CvtPrimOp op) {
4389 auto operand = getLoweredValue(op.getOperand());
4393 if (type_cast<IntType>(op.getOperand().getType()).isUnsigned())
4394 return setLowering(op, getOrCreateIntConstant(1, 0));
4396 return setLowering(op, Value());
4401 if (type_cast<IntType>(op.getOperand().getType()).isSigned())
4402 return setLowering(op, operand);
4405 auto zero = getOrCreateIntConstant(1, 0);
4406 return setLoweringTo<comb::ConcatOp>(op, zero, operand);
4409LogicalResult FIRRTLLowering::visitExpr(NotPrimOp op) {
4410 auto operand = getLoweredValue(op.getInput());
4414 auto allOnes = getOrCreateIntConstant(
4415 APInt::getAllOnes(operand.getType().getIntOrFloatBitWidth()));
4416 return setLoweringTo<comb::XorOp>(op, operand, allOnes,
true);
4419LogicalResult FIRRTLLowering::visitExpr(NegPrimOp op) {
4422 auto operand = getLoweredAndExtendedValue(op.getInput(), op.getType());
4426 auto resultType =
lowerType(op.getType());
4428 auto zero = getOrCreateIntConstant(resultType.getIntOrFloatBitWidth(), 0);
4429 return setLoweringTo<comb::SubOp>(op, zero, operand,
true);
4433LogicalResult FIRRTLLowering::visitExpr(PadPrimOp op) {
4434 auto operand = getLoweredAndExtendedValue(op.getInput(), op.getType());
4437 return setLowering(op, operand);
4440LogicalResult FIRRTLLowering::visitExpr(XorRPrimOp op) {
4441 auto operand = getLoweredValue(op.getInput());
4444 return setLowering(op, getOrCreateIntConstant(1, 0));
4449 return setLoweringTo<comb::ParityOp>(op, builder.getIntegerType(1), operand,
4453LogicalResult FIRRTLLowering::visitExpr(AndRPrimOp op) {
4454 auto operand = getLoweredValue(op.getInput());
4457 return setLowering(op, getOrCreateIntConstant(1, 1));
4462 return setLoweringTo<comb::ICmpOp>(
4463 op, ICmpPredicate::eq, operand,
4464 getOrCreateIntConstant(
4465 APInt::getAllOnes(operand.getType().getIntOrFloatBitWidth())),
4469LogicalResult FIRRTLLowering::visitExpr(OrRPrimOp op) {
4470 auto operand = getLoweredValue(op.getInput());
4473 return setLowering(op, getOrCreateIntConstant(1, 0));
4479 return setLoweringTo<comb::ICmpOp>(
4480 op, ICmpPredicate::ne, operand,
4481 getOrCreateIntConstant(operand.getType().getIntOrFloatBitWidth(), 0),
4489template <
typename ResultOpType>
4490LogicalResult FIRRTLLowering::lowerBinOpToVariadic(Operation *op) {
4491 auto resultType = op->getResult(0).getType();
4492 auto lhs = getLoweredAndExtendedValue(op->getOperand(0), resultType);
4493 auto rhs = getLoweredAndExtendedValue(op->getOperand(1), resultType);
4497 return setLoweringTo<ResultOpType>(op, lhs, rhs,
true);
4503template <
typename ResultOpType>
4504LogicalResult FIRRTLLowering::lowerElementwiseLogicalOp(Operation *op) {
4505 auto resultType = op->getResult(0).getType();
4506 auto lhs = getLoweredAndExtendedValue(op->getOperand(0), resultType);
4507 auto rhs = getLoweredAndExtendedValue(op->getOperand(1), resultType);
4518 auto intType = builder.getIntegerType(*bitwidth);
4519 auto retType = lhs.getType();
4522 auto result = builder.createOrFold<ResultOpType>(lhs, rhs,
true);
4523 return setLoweringTo<hw::BitcastOp>(op, retType, result);
4528template <
typename ResultUn
signedOpType,
typename ResultSignedOpType>
4529LogicalResult FIRRTLLowering::lowerBinOp(Operation *op) {
4531 auto resultType = op->getResult(0).getType();
4532 auto lhs = getLoweredAndExtendedValue(op->getOperand(0), resultType);
4533 auto rhs = getLoweredAndExtendedValue(op->getOperand(1), resultType);
4538 if (type_cast<IntType>(resultType).isSigned())
4539 return setLoweringTo<ResultSignedOpType>(op, lhs, rhs,
true);
4540 return setLoweringTo<ResultUnsignedOpType>(op, lhs, rhs,
true);
4545LogicalResult FIRRTLLowering::lowerCmpOp(Operation *op, ICmpPredicate signedOp,
4546 ICmpPredicate unsignedOp) {
4548 auto lhsIntType = type_cast<IntType>(op->getOperand(0).getType());
4549 auto rhsIntType = type_cast<IntType>(op->getOperand(1).getType());
4550 if (!lhsIntType.hasWidth() || !rhsIntType.hasWidth())
4554 if (cmpType.getWidth() == 0)
4555 cmpType = UIntType::get(builder.getContext(), 1);
4556 auto lhs = getLoweredAndExtendedValue(op->getOperand(0), cmpType);
4557 auto rhs = getLoweredAndExtendedValue(op->getOperand(1), cmpType);
4562 Type resultType = builder.getIntegerType(1);
4563 return setLoweringTo<comb::ICmpOp>(
4564 op, resultType, lhsIntType.isSigned() ? signedOp : unsignedOp, lhs, rhs,
4570template <
typename SignedOp,
typename Un
signedOp>
4571LogicalResult FIRRTLLowering::lowerDivLikeOp(Operation *op) {
4575 auto opType = type_cast<IntType>(op->getResult(0).getType());
4576 if (opType.getWidth() == 0)
4577 return setLowering(op->getResult(0), Value());
4581 auto lhs = getLoweredAndExtendedValue(op->getOperand(0), resultType);
4582 auto rhs = getLoweredAndExtendedValue(op->getOperand(1), resultType);
4587 if (opType.isSigned())
4588 result = builder.createOrFold<SignedOp>(lhs, rhs,
true);
4590 result = builder.createOrFold<UnsignedOp>(lhs, rhs,
true);
4592 if (
auto *definingOp = result.getDefiningOp())
4595 if (resultType == opType)
4596 return setLowering(op->getResult(0), result);
4597 return setLoweringTo<comb::ExtractOp>(op,
lowerType(opType), result, 0);
4600LogicalResult FIRRTLLowering::visitExpr(CatPrimOp op) {
4602 if (op.getInputs().empty())
4603 return setLowering(op, Value());
4605 SmallVector<Value> loweredOperands;
4608 for (
auto operand : op.getInputs()) {
4609 auto loweredOperand = getLoweredValue(operand);
4610 if (loweredOperand) {
4611 loweredOperands.push_back(loweredOperand);
4614 auto result =
handleZeroBit(operand, [&]() {
return success(); });
4622 if (loweredOperands.empty())
4623 return setLowering(op, Value());
4626 return setLoweringTo<comb::ConcatOp>(op, loweredOperands);
4633LogicalResult FIRRTLLowering::visitExpr(IsXIntrinsicOp op) {
4634 auto input = getLoweredNonClockValue(op.getArg());
4638 if (!isa<IntType>(input.getType())) {
4639 auto srcType = op.getArg().getType();
4641 assert(bitwidth &&
"Unknown width");
4642 auto intType = builder.getIntegerType(*bitwidth);
4643 input = builder.createOrFold<
hw::BitcastOp>(intType, input);
4646 return setLoweringTo<comb::ICmpOp>(
4647 op, ICmpPredicate::ceq, input,
4648 getOrCreateXConstant(input.getType().getIntOrFloatBitWidth()),
true);
4651LogicalResult FIRRTLLowering::visitStmt(FPGAProbeIntrinsicOp op) {
4652 auto operand = getLoweredValue(op.getInput());
4653 hw::WireOp::create(builder, operand);
4657LogicalResult FIRRTLLowering::visitExpr(PlusArgsTestIntrinsicOp op) {
4658 return setLoweringTo<sim::PlusArgsTestOp>(op, builder.getIntegerType(1),
4659 op.getFormatStringAttr());
4662LogicalResult FIRRTLLowering::visitExpr(PlusArgsValueIntrinsicOp op) {
4663 auto type =
lowerType(op.getResult().getType());
4667 auto valueOp = sim::PlusArgsValueOp::create(
4668 builder, builder.getIntegerType(1), type, op.getFormatStringAttr());
4669 if (failed(setLowering(op.getResult(), valueOp.getResult())))
4671 if (failed(setLowering(op.getFound(), valueOp.getFound())))
4676LogicalResult FIRRTLLowering::visitExpr(SizeOfIntrinsicOp op) {
4677 op.emitError(
"SizeOf should have been resolved.");
4681LogicalResult FIRRTLLowering::visitExpr(ClockGateIntrinsicOp op) {
4683 if (op.getTestEnable())
4684 testEnable = getLoweredValue(op.getTestEnable());
4685 return setLoweringTo<seq::ClockGateOp>(
4686 op, getLoweredValue(op.getInput()), getLoweredValue(op.getEnable()),
4687 testEnable, hw::InnerSymAttr{});
4690LogicalResult FIRRTLLowering::visitExpr(ClockInverterIntrinsicOp op) {
4691 auto operand = getLoweredValue(op.getInput());
4692 return setLoweringTo<seq::ClockInverterOp>(op, operand);
4695LogicalResult FIRRTLLowering::visitExpr(ClockDividerIntrinsicOp op) {
4696 auto operand = getLoweredValue(op.getInput());
4697 return setLoweringTo<seq::ClockDividerOp>(op, operand, op.getPow2());
4700LogicalResult FIRRTLLowering::visitExpr(LTLAndIntrinsicOp op) {
4701 return setLoweringToLTL<ltl::AndOp>(
4703 ValueRange{getLoweredValue(op.getLhs()), getLoweredValue(op.getRhs())});
4706LogicalResult FIRRTLLowering::visitExpr(LTLOrIntrinsicOp op) {
4707 return setLoweringToLTL<ltl::OrOp>(
4709 ValueRange{getLoweredValue(op.getLhs()), getLoweredValue(op.getRhs())});
4712LogicalResult FIRRTLLowering::visitExpr(LTLIntersectIntrinsicOp op) {
4713 return setLoweringToLTL<ltl::IntersectOp>(
4715 ValueRange{getLoweredValue(op.getLhs()), getLoweredValue(op.getRhs())});
4718LogicalResult FIRRTLLowering::visitExpr(LTLDelayIntrinsicOp op) {
4719 return setLoweringToLTL<ltl::DelayOp>(op, getLoweredValue(op.getInput()),
4720 op.getDelayAttr(), op.getLengthAttr());
4723LogicalResult FIRRTLLowering::visitExpr(LTLConcatIntrinsicOp op) {
4724 return setLoweringToLTL<ltl::ConcatOp>(
4726 ValueRange{getLoweredValue(op.getLhs()), getLoweredValue(op.getRhs())});
4729LogicalResult FIRRTLLowering::visitExpr(LTLRepeatIntrinsicOp op) {
4730 return setLoweringToLTL<ltl::RepeatOp>(op, getLoweredValue(op.getInput()),
4731 op.getBaseAttr(), op.getMoreAttr());
4734LogicalResult FIRRTLLowering::visitExpr(LTLGoToRepeatIntrinsicOp op) {
4735 return setLoweringToLTL<ltl::GoToRepeatOp>(
4736 op, getLoweredValue(op.getInput()), op.getBaseAttr(), op.getMoreAttr());
4739LogicalResult FIRRTLLowering::visitExpr(LTLNonConsecutiveRepeatIntrinsicOp op) {
4740 return setLoweringToLTL<ltl::NonConsecutiveRepeatOp>(
4741 op, getLoweredValue(op.getInput()), op.getBaseAttr(), op.getMoreAttr());
4744LogicalResult FIRRTLLowering::visitExpr(LTLNotIntrinsicOp op) {
4745 return setLoweringToLTL<ltl::NotOp>(op, getLoweredValue(op.getInput()));
4748LogicalResult FIRRTLLowering::visitExpr(LTLImplicationIntrinsicOp op) {
4749 return setLoweringToLTL<ltl::ImplicationOp>(
4751 ValueRange{getLoweredValue(op.getLhs()), getLoweredValue(op.getRhs())});
4754LogicalResult FIRRTLLowering::visitExpr(LTLUntilIntrinsicOp op) {
4755 return setLoweringToLTL<ltl::UntilOp>(
4757 ValueRange{getLoweredValue(op.getLhs()), getLoweredValue(op.getRhs())});
4760LogicalResult FIRRTLLowering::visitExpr(LTLEventuallyIntrinsicOp op) {
4761 return setLoweringToLTL<ltl::EventuallyOp>(op,
4762 getLoweredValue(op.getInput()));
4765LogicalResult FIRRTLLowering::visitExpr(LTLClockIntrinsicOp op) {
4766 return setLoweringToLTL<ltl::ClockOp>(op, getLoweredValue(op.getInput()),
4767 ltl::ClockEdge::Pos,
4768 getLoweredNonClockValue(op.getClock()));
4771template <
typename TargetOp,
typename IntrinsicOp>
4772LogicalResult FIRRTLLowering::lowerVerifIntrinsicOp(IntrinsicOp op) {
4773 auto property = getLoweredValue(op.getProperty());
4774 auto enable = op.getEnable() ? getLoweredValue(op.getEnable()) : Value();
4775 TargetOp::create(builder, property, enable, op.getLabelAttr());
4779LogicalResult FIRRTLLowering::visitStmt(VerifAssertIntrinsicOp op) {
4780 return lowerVerifIntrinsicOp<verif::AssertOp>(op);
4783LogicalResult FIRRTLLowering::visitStmt(VerifAssumeIntrinsicOp op) {
4784 return lowerVerifIntrinsicOp<verif::AssumeOp>(op);
4787LogicalResult FIRRTLLowering::visitStmt(VerifCoverIntrinsicOp op) {
4788 return lowerVerifIntrinsicOp<verif::CoverOp>(op);
4791LogicalResult FIRRTLLowering::visitStmt(VerifRequireIntrinsicOp op) {
4792 if (!isa<verif::ContractOp>(op->getParentOp()))
4793 return lowerVerifIntrinsicOp<verif::AssertOp>(op);
4794 return lowerVerifIntrinsicOp<verif::RequireOp>(op);
4797LogicalResult FIRRTLLowering::visitStmt(VerifEnsureIntrinsicOp op) {
4798 if (!isa<verif::ContractOp>(op->getParentOp()))
4799 return lowerVerifIntrinsicOp<verif::AssertOp>(op);
4800 return lowerVerifIntrinsicOp<verif::EnsureOp>(op);
4803LogicalResult FIRRTLLowering::visitExpr(HasBeenResetIntrinsicOp op) {
4804 auto clock = getLoweredNonClockValue(op.getClock());
4805 auto reset = getLoweredValue(op.getReset());
4806 if (!clock || !reset)
4808 auto resetType = op.getReset().getType();
4809 auto uintResetType = dyn_cast<UIntType>(resetType);
4810 auto isSync = uintResetType && uintResetType.getWidth() == 1;
4811 auto isAsync = isa<AsyncResetType>(resetType);
4812 if (!isAsync && !isSync) {
4813 auto d = op.emitError(
"uninferred reset passed to 'has_been_reset'; "
4814 "requires sync or async reset");
4815 d.attachNote() <<
"reset is of type " << resetType
4816 <<
", should be '!firrtl.uint<1>' or '!firrtl.asyncreset'";
4819 return setLoweringTo<verif::HasBeenResetOp>(op, clock, reset, isAsync);
4826LogicalResult FIRRTLLowering::visitExpr(BitsPrimOp op) {
4827 auto input = getLoweredValue(op.getInput());
4831 Type resultType = builder.getIntegerType(op.getHi() - op.getLo() + 1);
4832 return setLoweringTo<comb::ExtractOp>(op, resultType, input, op.getLo());
4835LogicalResult FIRRTLLowering::visitExpr(InvalidValueOp op) {
4836 auto resultTy =
lowerType(op.getType());
4843 if (type_isa<AnalogType>(op.getType()))
4846 return setLoweringTo<sv::WireOp>(op, resultTy,
".invalid_analog");
4849 if (type_cast<FIRRTLBaseType>(op.getType()).containsAnalog())
4860 auto constant = getOrCreateIntConstant(*bitwidth, 0);
4862 if (!type_isa<IntegerType>(resultTy))
4864 return setLowering(op, constant);
4868 op.emitOpError(
"unsupported type");
4872LogicalResult FIRRTLLowering::visitExpr(HeadPrimOp op) {
4873 auto input = getLoweredValue(op.getInput());
4876 auto inWidth = type_cast<IntegerType>(input.getType()).getWidth();
4877 if (op.getAmount() == 0)
4878 return setLowering(op, Value());
4879 Type resultType = builder.getIntegerType(op.getAmount());
4880 return setLoweringTo<comb::ExtractOp>(op, resultType, input,
4881 inWidth - op.getAmount());
4884LogicalResult FIRRTLLowering::visitExpr(ShlPrimOp op) {
4885 auto input = getLoweredValue(op.getInput());
4888 if (op.getAmount() == 0)
4890 return setLowering(op, getOrCreateIntConstant(op.getAmount(), 0));
4895 if (op.getAmount() == 0)
4896 return setLowering(op, input);
4898 auto zero = getOrCreateIntConstant(op.getAmount(), 0);
4899 return setLoweringTo<comb::ConcatOp>(op, input, zero);
4902LogicalResult FIRRTLLowering::visitExpr(ShrPrimOp op) {
4903 auto input = getLoweredValue(op.getInput());
4908 auto inWidth = type_cast<IntegerType>(input.getType()).getWidth();
4909 auto shiftAmount = op.getAmount();
4910 if (shiftAmount >= inWidth) {
4912 if (type_cast<IntType>(op.getInput().getType()).isUnsigned())
4913 return setLowering(op, {});
4916 shiftAmount = inWidth - 1;
4919 Type resultType = builder.getIntegerType(inWidth - shiftAmount);
4920 return setLoweringTo<comb::ExtractOp>(op, resultType, input, shiftAmount);
4923LogicalResult FIRRTLLowering::visitExpr(TailPrimOp op) {
4924 auto input = getLoweredValue(op.getInput());
4928 auto inWidth = type_cast<IntegerType>(input.getType()).getWidth();
4929 if (inWidth == op.getAmount())
4930 return setLowering(op, Value());
4931 Type resultType = builder.getIntegerType(inWidth - op.getAmount());
4932 return setLoweringTo<comb::ExtractOp>(op, resultType, input, 0);
4935LogicalResult FIRRTLLowering::visitExpr(MuxPrimOp op) {
4936 auto cond = getLoweredValue(op.getSel());
4937 auto ifTrue = getLoweredAndExtendedValue(op.getHigh(), op.getType());
4938 auto ifFalse = getLoweredAndExtendedValue(op.getLow(), op.getType());
4939 if (!cond || !ifTrue || !ifFalse)
4942 if (isa<ClockType>(op.getType()))
4943 return setLoweringTo<seq::ClockMuxOp>(op, cond, ifTrue, ifFalse);
4944 return setLoweringTo<comb::MuxOp>(op, ifTrue.getType(), cond, ifTrue, ifFalse,
4948LogicalResult FIRRTLLowering::visitExpr(Mux2CellIntrinsicOp op) {
4949 auto cond = getLoweredValue(op.getSel());
4950 auto ifTrue = getLoweredAndExtendedValue(op.getHigh(), op.getType());
4951 auto ifFalse = getLoweredAndExtendedValue(op.getLow(), op.getType());
4952 if (!cond || !ifTrue || !ifFalse)
4955 auto val = comb::MuxOp::create(builder, ifTrue.getType(), cond, ifTrue,
4957 return setLowering(op, createValueWithMuxAnnotation(val,
true));
4960LogicalResult FIRRTLLowering::visitExpr(Mux4CellIntrinsicOp op) {
4961 auto sel = getLoweredValue(op.getSel());
4962 auto v3 = getLoweredAndExtendedValue(op.getV3(), op.getType());
4963 auto v2 = getLoweredAndExtendedValue(op.getV2(), op.getType());
4964 auto v1 = getLoweredAndExtendedValue(op.getV1(), op.getType());
4965 auto v0 = getLoweredAndExtendedValue(op.getV0(), op.getType());
4966 if (!sel || !v3 || !v2 || !v1 || !v0)
4968 Value array[] = {v3, v2, v1, v0};
4971 return setLowering(op, createValueWithMuxAnnotation(val,
false));
4990Value FIRRTLLowering::createValueWithMuxAnnotation(Operation *op,
bool isMux2) {
4991 assert(op->getNumResults() == 1 &&
"only expect a single result");
4992 auto val = op->getResult(0);
4996 op, sv::SVAttributeAttr::get(builder.getContext(),
"cadence map_to_mux",
5003 OpBuilder::InsertionGuard guard(builder);
5004 builder.setInsertionPoint(op);
5005 StringRef namehint = isMux2 ?
"mux2cell_in" :
"mux4cell_in";
5006 for (
auto [idx, operand] :
llvm::enumerate(op->getOperands())) {
5008 op->getContext(),
nullptr, 0,
5011 hw::WireOp::create(builder, operand, namehint + Twine(idx), innerSym);
5012 op->setOperand(idx, wire);
5017 sv::setSVAttributes(assignOp,
5018 sv::SVAttributeAttr::get(builder.getContext(),
5019 "synopsys infer_mux_override",
5024Value FIRRTLLowering::createArrayIndexing(Value array, Value index) {
5026 auto size = hw::type_cast<hw::ArrayType>(array.getType()).getNumElements();
5031 if (!llvm::isPowerOf2_64(size)) {
5032 auto extElem = getOrCreateIntConstant(APInt(llvm::Log2_64_Ceil(size), 0));
5034 SmallVector<Value> temp(llvm::NextPowerOf2(size) - size, extValue);
5036 Value temp2[] = {ext.getResult(), array};
5042 return inBoundsRead;
5045LogicalResult FIRRTLLowering::visitExpr(MultibitMuxOp op) {
5047 auto index = getLoweredAndExtOrTruncValue(
5049 UIntType::get(op.getContext(),
5054 SmallVector<Value> loweredInputs;
5055 loweredInputs.reserve(op.getInputs().size());
5056 for (
auto input : op.getInputs()) {
5057 auto lowered = getLoweredAndExtendedValue(input, op.getType());
5060 loweredInputs.push_back(lowered);
5064 return setLowering(op, createArrayIndexing(array, index));
5067LogicalResult FIRRTLLowering::visitExpr(VerbatimExprOp op) {
5068 auto resultTy =
lowerType(op.getType());
5072 SmallVector<Value, 4> operands;
5073 operands.reserve(op.getSubstitutions().size());
5074 for (
auto operand : op.getSubstitutions()) {
5075 auto lowered = getLoweredValue(operand);
5078 operands.push_back(lowered);
5081 ArrayAttr symbols = op.getSymbolsAttr();
5083 symbols = ArrayAttr::get(op.getContext(), {});
5085 return setLoweringTo<sv::VerbatimExprOp>(op, resultTy, op.getTextAttr(),
5089LogicalResult FIRRTLLowering::visitExpr(XMRRefOp op) {
5093 Type baseType = op.getType().getType();
5096 if (isa<ClockType>(baseType))
5097 xmrType = builder.getIntegerType(1);
5101 return setLoweringTo<sv::XMRRefOp>(op, sv::InOutType::get(xmrType),
5102 op.getRef(), op.getVerbatimSuffixAttr());
5105LogicalResult FIRRTLLowering::visitExpr(XMRDerefOp op) {
5109 if (isa<ClockType>(op.getType()))
5110 xmrType = builder.getIntegerType(1);
5114 auto xmr = sv::XMRRefOp::create(builder, sv::InOutType::get(xmrType),
5115 op.getRef(), op.getVerbatimSuffixAttr());
5116 auto readXmr = getReadValue(xmr);
5117 if (!isa<ClockType>(op.getType()))
5118 return setLowering(op, readXmr);
5119 return setLoweringTo<seq::ToClockOp>(op, readXmr);
5124LogicalResult FIRRTLLowering::visitExpr(TimeOp op) {
return success(); }
5125LogicalResult FIRRTLLowering::visitExpr(HierarchicalModuleNameOp op) {
5133LogicalResult FIRRTLLowering::visitStmt(SkipOp op) {
5145FailureOr<bool> FIRRTLLowering::lowerConnect(Value destVal, Value srcVal) {
5146 auto srcType = srcVal.getType();
5147 auto dstType = destVal.getType();
5148 if (srcType != dstType &&
5149 (isa<hw::TypeAliasType>(srcType) || isa<hw::TypeAliasType>(dstType))) {
5152 return TypeSwitch<Operation *, FailureOr<bool>>(destVal.getDefiningOp())
5153 .Case<hw::WireOp>([&](
auto op) {
5154 maybeUnused(op.getInput());
5155 op.getInputMutable().assign(srcVal);
5158 .Case<seq::FirRegOp>([&](
auto op) {
5159 maybeUnused(op.getNext());
5160 op.getNextMutable().assign(srcVal);
5163 .Case<hw::StructExtractOp, hw::ArrayGetOp>([](
auto op) {
5166 op.emitOpError(
"used as connect destination");
5169 .Default([](
auto) {
return false; });
5172LogicalResult FIRRTLLowering::visitStmt(ConnectOp op) {
5173 auto dest = op.getDest();
5175 auto destType = type_cast<FIRRTLBaseType>(dest.getType()).getPassiveType();
5176 auto srcVal = getLoweredAndExtendedValue(op.getSrc(), destType);
5178 return handleZeroBit(op.getSrc(), []() { return success(); });
5180 auto destVal = getPossiblyInoutLoweredValue(dest);
5184 auto result = lowerConnect(destVal, srcVal);
5192 if (updateIfBackedge(destVal, srcVal))
5195 if (!isa<hw::InOutType>(destVal.getType()))
5196 return op.emitError(
"destination isn't an inout type");
5202LogicalResult FIRRTLLowering::visitStmt(MatchingConnectOp op) {
5203 auto dest = op.getDest();
5204 auto srcVal = getLoweredValue(op.getSrc());
5206 return handleZeroBit(op.getSrc(), []() { return success(); });
5208 auto destVal = getPossiblyInoutLoweredValue(dest);
5212 auto result = lowerConnect(destVal, srcVal);
5220 if (updateIfBackedge(destVal, srcVal))
5223 if (!isa<hw::InOutType>(destVal.getType()))
5224 return op.emitError(
"destination isn't an inout type");
5230LogicalResult FIRRTLLowering::visitStmt(ForceOp op) {
5231 auto srcVal = getLoweredValue(op.getSrc());
5235 auto destVal = getPossiblyInoutLoweredValue(op.getDest());
5239 if (!isa<hw::InOutType>(destVal.getType()))
5240 return op.emitError(
"destination isn't an inout type");
5243 circuitState.addMacroDecl(builder.getStringAttr(
"SYNTHESIS"));
5244 addToIfDefBlock(
"SYNTHESIS", std::function<
void()>(), [&]() {
5245 addToInitialBlock([&]() { sv::ForceOp::create(builder, destVal, srcVal); });
5250LogicalResult FIRRTLLowering::visitStmt(RefForceOp op) {
5251 auto src = getLoweredNonClockValue(op.getSrc());
5252 auto clock = getLoweredNonClockValue(op.getClock());
5253 auto pred = getLoweredValue(op.getPredicate());
5254 if (!src || !clock || !pred)
5257 auto destVal = getPossiblyInoutLoweredValue(op.getDest());
5262 circuitState.addMacroDecl(builder.getStringAttr(
"SYNTHESIS"));
5263 addToIfDefBlock(
"SYNTHESIS", std::function<
void()>(), [&]() {
5264 addToAlwaysBlock(clock, [&]() {
5265 addIfProceduralBlock(
5266 pred, [&]() { sv::ForceOp::create(builder, destVal, src); });
5271LogicalResult FIRRTLLowering::visitStmt(RefForceInitialOp op) {
5272 auto src = getLoweredNonClockValue(op.getSrc());
5273 auto pred = getLoweredValue(op.getPredicate());
5277 auto destVal = getPossiblyInoutLoweredValue(op.getDest());
5282 circuitState.addMacroDecl(builder.getStringAttr(
"SYNTHESIS"));
5283 addToIfDefBlock(
"SYNTHESIS", std::function<
void()>(), [&]() {
5284 addToInitialBlock([&]() {
5285 addIfProceduralBlock(
5286 pred, [&]() { sv::ForceOp::create(builder, destVal, src); });
5291LogicalResult FIRRTLLowering::visitStmt(RefReleaseOp op) {
5292 auto clock = getLoweredNonClockValue(op.getClock());
5293 auto pred = getLoweredValue(op.getPredicate());
5294 if (!clock || !pred)
5297 auto destVal = getPossiblyInoutLoweredValue(op.getDest());
5302 circuitState.addMacroDecl(builder.getStringAttr(
"SYNTHESIS"));
5303 addToIfDefBlock(
"SYNTHESIS", std::function<
void()>(), [&]() {
5304 addToAlwaysBlock(clock, [&]() {
5305 addIfProceduralBlock(pred,
5306 [&]() { sv::ReleaseOp::create(builder, destVal); });
5311LogicalResult FIRRTLLowering::visitStmt(RefReleaseInitialOp op) {
5312 auto destVal = getPossiblyInoutLoweredValue(op.getDest());
5313 auto pred = getLoweredValue(op.getPredicate());
5314 if (!destVal || !pred)
5318 circuitState.addMacroDecl(builder.getStringAttr(
"SYNTHESIS"));
5319 addToIfDefBlock(
"SYNTHESIS", std::function<
void()>(), [&]() {
5320 addToInitialBlock([&]() {
5321 addIfProceduralBlock(pred,
5322 [&]() { sv::ReleaseOp::create(builder, destVal); });
5330 StringRef originalFormatString,
5331 ValueRange operands,
5332 StringAttr &result) {
5335 SmallString<32> formatString;
5336 for (
size_t i = 0, e = originalFormatString.size(), subIdx = 0; i != e; ++i) {
5337 char c = originalFormatString[i];
5341 formatString.push_back(c);
5344 SmallString<6> width;
5345 c = originalFormatString[++i];
5348 c = originalFormatString[++i];
5359 formatString.append(width);
5365 formatString.push_back(c);
5372 if (originalFormatString.slice(i, i + 4) !=
"{{}}") {
5373 formatString.push_back(c);
5377 auto substitution = operands[subIdx++];
5378 assert(type_isa<FStringType>(substitution.getType()) &&
5379 "the operand for a '{{}}' substitution must be an 'fstring' type");
5381 TypeSwitch<Operation *, LogicalResult>(substitution.getDefiningOp())
5382 .template Case<TimeOp>([&](
auto) {
5383 formatString.append(
"%0t");
5386 .
template Case<HierarchicalModuleNameOp>([&](
auto) {
5387 formatString.append(
"%m");
5390 .Default([&](
auto) {
5391 emitError(loc,
"has a substitution with an unimplemented "
5393 .attachNote(substitution.getLoc())
5394 <<
"op with an unimplemented lowering is here";
5404 formatString.push_back(c);
5408 result = StringAttr::get(loc->getContext(), formatString);
5415LogicalResult FIRRTLLowering::visitPrintfLike(
5416 T op,
const FileDescriptorInfo &fileDescriptorInfo,
bool usePrintfCond) {
5417 auto clock = getLoweredNonClockValue(op.getClock());
5418 auto cond = getLoweredValue(op.getCond());
5419 if (!clock || !cond)
5422 StringAttr formatString;
5424 op.getSubstitutions(), formatString)))
5427 auto fn = [&](Value fd) {
5428 SmallVector<Value> operands;
5429 if (failed(loweredFmtOperands(op.getSubstitutions(), operands)))
5431 sv::FWriteOp::create(builder, op.getLoc(), fd, formatString, operands);
5435 return lowerStatementWithFd(fileDescriptorInfo, clock, cond, fn,
5439LogicalResult FIRRTLLowering::visitStmt(FPrintFOp op) {
5440 StringAttr outputFileAttr;
5442 op.getOutputFileSubstitutions(),
5446 FileDescriptorInfo outputFile(outputFileAttr,
5447 op.getOutputFileSubstitutions());
5448 return visitPrintfLike(op, outputFile,
false);
5452LogicalResult FIRRTLLowering::visitStmt(FFlushOp op) {
5453 auto clock = getLoweredNonClockValue(op.getClock());
5454 auto cond = getLoweredValue(op.getCond());
5455 if (!clock || !cond)
5458 auto fn = [&](Value fd) {
5459 sv::FFlushOp::create(builder, op.getLoc(), fd);
5463 if (!op.getOutputFileAttr())
5464 return lowerStatementWithFd({}, clock, cond, fn,
false);
5468 StringAttr outputFileAttr;
5470 op.getOutputFileSubstitutions(),
5474 return lowerStatementWithFd(
5475 FileDescriptorInfo(outputFileAttr, op.getOutputFileSubstitutions()),
5476 clock, cond, fn,
false);
5481LogicalResult FIRRTLLowering::visitStmt(StopOp op) {
5482 auto clock = getLoweredValue(op.getClock());
5483 auto cond = getLoweredValue(op.getCond());
5484 if (!clock || !cond)
5487 circuitState.usedStopCond =
true;
5488 circuitState.addFragment(theModule,
"STOP_COND_FRAGMENT");
5491 sv::MacroRefExprOp::create(builder, cond.getType(),
"STOP_COND_");
5492 Value exitCond = builder.createOrFold<
comb::AndOp>(stopCond, cond,
true);
5494 sim::ClockedTerminateOp::create(builder, clock, exitCond,
5495 op.getExitCode() == 0,
5504template <
typename... Args>
5506 StringRef opName, Args &&...args) {
5507 if (opName ==
"assert")
5508 return sv::AssertOp::create(builder, std::forward<Args>(args)...);
5509 if (opName ==
"assume")
5510 return sv::AssumeOp::create(builder, std::forward<Args>(args)...);
5511 if (opName ==
"cover")
5512 return sv::CoverOp::create(builder, std::forward<Args>(args)...);
5513 llvm_unreachable(
"unknown verification op");
5519template <
typename... Args>
5521 StringRef opName, Args &&...args) {
5522 if (opName ==
"assert")
5523 return sv::AssertConcurrentOp::create(builder, std::forward<Args>(args)...);
5524 if (opName ==
"assume")
5525 return sv::AssumeConcurrentOp::create(builder, std::forward<Args>(args)...);
5526 if (opName ==
"cover")
5527 return sv::CoverConcurrentOp::create(builder, std::forward<Args>(args)...);
5528 llvm_unreachable(
"unknown verification op");
5549LogicalResult FIRRTLLowering::lowerVerificationStatement(
5550 Operation *op, StringRef labelPrefix, Value opClock, Value opPredicate,
5551 Value opEnable, StringAttr opMessageAttr, ValueRange opOperands,
5552 StringAttr opNameAttr,
bool isConcurrent, EventControl opEventControl) {
5553 StringRef opName = op->getName().stripDialect();
5556 ArrayRef<Attribute> guards{};
5557 if (
auto guardsAttr = op->template getAttrOfType<ArrayAttr>(
"guards"))
5558 guards = guardsAttr.getValue();
5560 auto isCover = isa<CoverOp>(op);
5561 auto clock = getLoweredNonClockValue(opClock);
5562 auto enable = getLoweredValue(opEnable);
5563 auto predicate = getLoweredValue(opPredicate);
5564 if (!clock || !enable || !predicate)
5568 if (opNameAttr && !opNameAttr.getValue().empty())
5570 StringAttr prefixedLabel;
5573 StringAttr::get(builder.getContext(), labelPrefix + label.getValue());
5576 SmallVector<Value> messageOps;
5580 if (flavor == VerificationFlavor::IfElseFatal && !isa<AssertOp>(op))
5581 flavor = VerificationFlavor::None;
5583 if (flavor == VerificationFlavor::None) {
5587 auto format = op->getAttrOfType<StringAttr>(
"format");
5589 if (isConcurrent && format && format.getValue() ==
"ifElseFatal") {
5590 if (!isa<AssertOp>(op))
5591 return op->emitError()
5592 <<
"ifElseFatal format cannot be used for non-assertions";
5593 flavor = VerificationFlavor::IfElseFatal;
5594 }
else if (isConcurrent)
5595 flavor = VerificationFlavor::SVA;
5597 flavor = VerificationFlavor::Immediate;
5600 if (!isCover && opMessageAttr && !opMessageAttr.getValue().empty()) {
5604 opOperands, message)))
5607 if (failed(loweredFmtOperands(opOperands, messageOps)))
5610 if (flavor == VerificationFlavor::SVA) {
5615 for (
auto &loweredValue : messageOps)
5616 loweredValue =
sv::SampledOp::create(builder, loweredValue);
5622 case VerificationFlavor::Immediate: {
5624 auto deferImmediate = circt::sv::DeferAssertAttr::get(
5625 builder.getContext(), circt::sv::DeferAssert::Immediate);
5626 addToAlwaysBlock(clock, [&]() {
5627 addIfProceduralBlock(enable, [&]() {
5629 prefixedLabel, message, messageOps);
5634 case VerificationFlavor::IfElseFatal: {
5635 assert(isa<AssertOp>(op) &&
"only assert is expected");
5638 auto boolType = IntegerType::get(builder.getContext(), 1);
5639 predicate = comb::createOrFoldNot(builder, predicate,
true);
5640 predicate = builder.createOrFold<
comb::AndOp>(enable, predicate,
true);
5642 circuitState.addMacroDecl(builder.getStringAttr(
"SYNTHESIS"));
5643 addToIfDefBlock(
"SYNTHESIS", {}, [&]() {
5644 addToAlwaysBlock(clock, [&]() {
5645 addIfProceduralBlock(predicate, [&]() {
5646 circuitState.usedStopCond =
true;
5647 circuitState.addFragment(theModule,
"STOP_COND_FRAGMENT");
5649 circuitState.usedAssertVerboseCond =
true;
5650 circuitState.addFragment(theModule,
"ASSERT_VERBOSE_COND_FRAGMENT");
5652 addIfProceduralBlock(
5653 sv::MacroRefExprOp::create(builder, boolType,
5654 "ASSERT_VERBOSE_COND_"),
5656 sv::ErrorProceduralOp::create(builder, message, messageOps);
5658 addIfProceduralBlock(
5659 sv::MacroRefExprOp::create(builder, boolType,
"STOP_COND_"),
5660 [&]() { sv::FatalProceduralOp::create(builder); });
5666 case VerificationFlavor::SVA: {
5671 comb::createOrFoldNot(builder, enable,
true);
5673 builder.createOrFold<
comb::OrOp>(notEnable, predicate,
true);
5675 predicate = builder.createOrFold<
comb::AndOp>(enable, predicate,
true);
5679 sv::EventControl event;
5680 switch (opEventControl) {
5681 case EventControl::AtPosEdge:
5682 event = circt::sv::EventControl::AtPosEdge;
5684 case EventControl::AtEdge:
5685 event = circt::sv::EventControl::AtEdge;
5687 case EventControl::AtNegEdge:
5688 event = circt::sv::EventControl::AtNegEdge;
5694 circt::sv::EventControlAttr::get(builder.getContext(), event), clock,
5695 predicate, prefixedLabel, message, messageOps);
5698 case VerificationFlavor::None:
5700 "flavor `None` must be converted into one of concreate flavors");
5707 return emitGuards(op->getLoc(), guards,
emit);
5711LogicalResult FIRRTLLowering::visitStmt(AssertOp op) {
5712 return lowerVerificationStatement(
5713 op,
"assert__", op.getClock(), op.getPredicate(), op.getEnable(),
5714 op.getMessageAttr(), op.getSubstitutions(), op.getNameAttr(),
5715 op.getIsConcurrent(), op.getEventControl());
5719LogicalResult FIRRTLLowering::visitStmt(AssumeOp op) {
5720 return lowerVerificationStatement(
5721 op,
"assume__", op.getClock(), op.getPredicate(), op.getEnable(),
5722 op.getMessageAttr(), op.getSubstitutions(), op.getNameAttr(),
5723 op.getIsConcurrent(), op.getEventControl());
5727LogicalResult FIRRTLLowering::visitStmt(CoverOp op) {
5728 return lowerVerificationStatement(
5729 op,
"cover__", op.getClock(), op.getPredicate(), op.getEnable(),
5730 op.getMessageAttr(), op.getSubstitutions(), op.getNameAttr(),
5731 op.getIsConcurrent(), op.getEventControl());
5735LogicalResult FIRRTLLowering::visitStmt(UnclockedAssumeIntrinsicOp op) {
5740 auto guardsAttr = op->getAttrOfType<mlir::ArrayAttr>(
"guards");
5741 ArrayRef<Attribute> guards =
5742 guardsAttr ? guardsAttr.getValue() : ArrayRef<Attribute>();
5744 auto label = op.getNameAttr();
5745 StringAttr assumeLabel;
5746 if (label && !label.empty())
5748 StringAttr::get(builder.getContext(),
"assume__" + label.getValue());
5749 auto predicate = getLoweredValue(op.getPredicate());
5750 auto enable = getLoweredValue(op.getEnable());
5751 auto notEnable = comb::createOrFoldNot(builder, enable,
true);
5752 predicate = builder.createOrFold<
comb::OrOp>(notEnable, predicate,
true);
5754 SmallVector<Value> messageOps;
5755 for (
auto operand : op.getSubstitutions()) {
5756 auto loweredValue = getLoweredValue(operand);
5757 if (!loweredValue) {
5761 loweredValue = getOrCreateIntConstant(1, 0);
5763 messageOps.push_back(loweredValue);
5765 return emitGuards(op.getLoc(), guards, [&]() {
5766 sv::AlwaysOp::create(
5767 builder, ArrayRef(sv::EventControl::AtEdge), ArrayRef(predicate),
5769 if (op.getMessageAttr().getValue().empty())
5770 buildImmediateVerifOp(
5771 builder,
"assume", predicate,
5772 circt::sv::DeferAssertAttr::get(
5773 builder.getContext(), circt::sv::DeferAssert::Immediate),
5776 buildImmediateVerifOp(
5777 builder,
"assume", predicate,
5778 circt::sv::DeferAssertAttr::get(
5779 builder.getContext(), circt::sv::DeferAssert::Immediate),
5780 assumeLabel, op.getMessageAttr(), messageOps);
5785LogicalResult FIRRTLLowering::visitStmt(AttachOp op) {
5787 if (op.getAttached().size() < 2)
5790 SmallVector<Value, 4> inoutValues;
5791 for (
auto v : op.getAttached()) {
5792 inoutValues.push_back(getPossiblyInoutLoweredValue(v));
5793 if (!inoutValues.back()) {
5797 inoutValues.pop_back();
5801 if (!isa<hw::InOutType>(inoutValues.back().getType()))
5802 return op.emitError(
"operand isn't an inout type");
5805 if (inoutValues.size() < 2)
5816 bool isAttachInternalOnly =
5817 llvm::none_of(inoutValues, [](
auto v) {
return isa<BlockArgument>(v); });
5819 if (isAttachInternalOnly) {
5820 auto v0 = inoutValues.front();
5821 for (
auto v : inoutValues) {
5824 v.replaceAllUsesWith(v0);
5831 circuitState.addMacroDecl(builder.getStringAttr(
"SYNTHESIS"));
5832 circuitState.addMacroDecl(builder.getStringAttr(
"VERILATOR"));
5837 SmallVector<Value, 4> values;
5838 for (
auto inoutValue : inoutValues)
5839 values.push_back(getReadValue(inoutValue));
5841 for (
size_t i1 = 0, e = inoutValues.size(); i1 != e; ++i1) {
5842 for (
size_t i2 = 0; i2 != e; ++i2)
5850 sv::IfDefOp::create(
5851 builder,
"VERILATOR",
5853 sv::VerbatimOp::create(
5855 "`error \"Verilator does not support alias and thus "
5857 "arbitrarily connect bidirectional wires and ports\"");
5859 [&]() { sv::AliasOp::create(builder, inoutValues); });
5865LogicalResult FIRRTLLowering::visitStmt(BindOp op) {
5866 sv::BindOp::create(builder, op.getInstanceAttr());
5870LogicalResult FIRRTLLowering::fixupLTLOps() {
5871 if (ltlOpFixupWorklist.empty())
5873 LLVM_DEBUG(llvm::dbgs() <<
"Fixing up " << ltlOpFixupWorklist.size()
5877 for (
unsigned i = 0, e = ltlOpFixupWorklist.size(); i != e; ++i)
5878 for (
auto *user : ltlOpFixupWorklist[i]->getUsers())
5879 if (isa<
hw::WireOp>(user))
5880 ltlOpFixupWorklist.insert(user);
5883 while (!ltlOpFixupWorklist.empty()) {
5884 auto *op = ltlOpFixupWorklist.pop_back_val();
5887 if (
auto opIntf = dyn_cast_or_null<mlir::InferTypeOpInterface>(op)) {
5888 LLVM_DEBUG(llvm::dbgs() <<
"- Update " << *op <<
"\n");
5889 SmallVector<Type, 2> types;
5890 auto result = opIntf.inferReturnTypes(
5891 op->getContext(), op->getLoc(), op->getOperands(),
5892 op->getAttrDictionary(), op->getPropertiesStorage(), op->getRegions(),
5896 assert(types.size() == op->getNumResults());
5900 for (
auto [result, type] :
llvm::zip(op->getResults(), types)) {
5901 if (result.getType() == type)
5903 LLVM_DEBUG(llvm::dbgs()
5904 <<
" - Result #" << result.getResultNumber() <<
" from "
5905 << result.getType() <<
" to " << type <<
"\n");
5906 result.setType(type);
5907 for (
auto *user : result.getUsers())
5909 ltlOpFixupWorklist.insert(user);
5914 if (
auto wireOp = dyn_cast<hw::WireOp>(op)) {
5915 if (isa<ltl::SequenceType, ltl::PropertyType>(wireOp.getType())) {
5916 wireOp.replaceAllUsesWith(wireOp.getInput());
5917 LLVM_DEBUG(llvm::dbgs() <<
"- Remove " << wireOp <<
"\n");
5918 if (wireOp.use_empty())
5925 SmallPtrSet<Operation *, 4> usersReported;
5926 for (
auto *user : op->getUsers()) {
5927 if (!usersReported.insert(user).second)
5929 if (isa_and_nonnull<ltl::LTLDialect, verif::VerifDialect>(
5930 user->getDialect()))
5932 if (isa<hw::WireOp>(user))
5934 auto d = op->emitError(
5935 "verification operation used in a non-verification context");
5936 d.attachNote(user->getLoc())
5937 <<
"leaking outside verification context here";
assert(baseType &&"element must be base type")
static std::unique_ptr< Context > context
static LogicalResult emitFile(ArrayRef< Operation * > operations, StringRef filePath, raw_ostream &os)
Emits the given operation to a file represented by the passed ostream and file-path.
static void lowerModuleBody(FModuleOp mod, const DenseMap< StringAttr, PortConversion > &ports)
static Operation * buildImmediateVerifOp(ImplicitLocOpBuilder &builder, StringRef opName, Args &&...args)
Helper function to build an immediate assert operation based on the original FIRRTL operation name.
static Operation * buildConcurrentVerifOp(ImplicitLocOpBuilder &builder, StringRef opName, Args &&...args)
Helper function to build a concurrent assert operation based on the original FIRRTL operation name.
static unsigned getBitWidthFromVectorSize(unsigned size)
static Value castToFIRRTLType(Value val, Type type, ImplicitLocOpBuilder &builder)
Cast a value to a desired target type.
static ArrayAttr getHWParameters(FExtModuleOp module, bool ignoreValues)
Map the parameter specifier on the specified extmodule into the HWModule representation for parameter...
static bool isZeroBitFIRRTLType(Type type)
Return true if the specified type is a sized FIRRTL type (Int or Analog) with zero bits.
static Value tryEliminatingAttachesToAnalogValue(Value value, Operation *insertPoint)
Given a value of analog type, check to see the only use of it is an attach.
static LogicalResult handleZeroBit(Value failedOperand, const std::function< LogicalResult()> &fn)
Zero bit operands end up looking like failures from getLoweredValue.
static const char moduleHierarchyFileAttrName[]
Attribute that indicates that the module hierarchy starting at the annotated module should be dumped ...
static void tryCopyName(Operation *dst, Operation *src)
static LogicalResult verifyOpLegality(Operation *op)
This verifies that the target operation has been lowered to a legal operation.
static Value castFromFIRRTLType(Value val, Type type, ImplicitLocOpBuilder &builder)
Cast from a FIRRTL type (potentially with a flip) to a standard type.
static SmallVector< SubfieldOp > getAllFieldAccesses(Value structValue, StringRef field)
static Value tryEliminatingConnectsToValue(Value flipValue, Operation *insertPoint, CircuitLoweringState &loweringState)
Given a value of flip type, check to see if all of the uses of it are connects.
static LogicalResult resolveFormatString(Location loc, StringRef originalFormatString, ValueRange operands, StringAttr &result)
static Value getSingleNonInstanceOperand(AttachOp op)
static IntType getWidestIntType(Type t1, Type t2)
Given two FIRRTL integer types, return the widest one.
static FailureOr< VectorizeOp > lowerBody(VectorizeOp op)
Vectorizes the body of the given arc.vectorize operation if it is not already vectorized.
static Location getLoc(DefSlot slot)
static StringAttr append(StringAttr base, const Twine &suffix)
Return a attribute with the specified suffix appended.
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.
void add(mlir::ModuleOp module)
StringRef newName(const Twine &name)
Return a unique name, derived from the input name, and add the new name to the internal namespace.
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 ...
FlatSymbolRefAttr getMacro(StringAttr optionName, StringAttr caseName) const
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 graph tracks modules and where they are instantiated.
This is an edge in the InstanceGraph.
create(elements, Type result_type=None)
create(str sym_name, Type type, str verilog_name=None)
create(data_type, name=None, sym_name=None)
Direction get(bool isOutput)
Returns an output direction if isOutput is true, otherwise returns an input direction.
StringRef getFragmentsAttrName()
Return the name of the fragments array attribute.
FIRRTLBaseType getBaseType(Type type)
If it is a base type, return it as is.
std::pair< hw::InnerSymAttr, StringAttr > getOrAddInnerSym(MLIRContext *context, hw::InnerSymAttr attr, uint64_t fieldID, llvm::function_ref< hw::InnerSymbolNamespace &()> getNamespace)
Ensure that the the InnerSymAttr has a symbol on the field specified.
bool hasDroppableName(Operation *op)
Return true if the name is droppable.
Type lowerType(Type type, std::optional< Location > loc={}, llvm::function_ref< hw::TypeAliasType(Type, BaseTypeAliasType, Location)> getTypeDeclFn={})
Given a type, return the corresponding lowered type for the HW dialect.
bool isExpression(Operation *op)
Return true if the specified operation is a firrtl expression.
std::optional< int64_t > getBitWidth(FIRRTLBaseType type, bool ignoreFlip=false)
StringAttr getName(ArrayAttr names, size_t idx)
Return the name at the specified index of the ArrayAttr or null if it cannot be determined.
void setSVAttributes(mlir::Operation *op, mlir::ArrayAttr attrs)
Set the SV attributes of an operation.
The InstanceGraph op interface, see InstanceGraphInterface.td for more details.
std::unique_ptr< mlir::Pass > createLowerFIRRTLToHWPass(bool enableAnnotationWarning=false, firrtl::VerificationFlavor assertionFlavor=firrtl::VerificationFlavor::None)
This is the pass constructor.
int run(Type[Generator] generator=CppGenerator, cmdline_args=sys.argv)
reg(value, clock, reset=None, reset_value=None, name=None, sym_name=None)
The namespace of a CircuitOp, generally inhabited by modules.
This holds the name and type that describes the module's ports.
bool isOutput() const
Return true if this is a simple output-only port.
AnnotationSet annotations
bool isInput() const
Return true if this is a simple input-only port.
This holds the name, type, direction of a module's ports.
size_t argNum
This is the argument index or the result index depending on the direction.
void setSym(InnerSymAttr sym, MLIRContext *ctx)
InnerSymAttr getSym() const