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);
167 StringRef annoClass, StringRef attrBase) {
169 auto *ctx = top.getContext();
172 if (
auto dir = anno.getMember<StringAttr>(
"directory")) {
173 SmallVector<NamedAttribute> old;
174 for (
auto i : top->getAttrs())
177 StringAttr::get(ctx, attrBase),
178 hw::OutputFileAttr::getAsDirectory(ctx, dir.getValue(),
true,
true));
181 if (
auto file = anno.getMember<StringAttr>(
"filename")) {
182 SmallVector<NamedAttribute> old;
183 for (
auto i : top->getAttrs())
185 old.emplace_back(StringAttr::get(ctx, attrBase +
".bindfile"),
186 hw::OutputFileAttr::getFromFilename(
187 ctx, file.getValue(),
true));
193 return size == 1 ? 1 : llvm::Log2_64_Ceil(size);
199 if (
auto attr = src->getAttrOfType<StringAttr>(
"name"))
200 if (!dst->hasAttr(
"sv.namehint") && !dst->hasAttr(
"name"))
201 dst->setAttr(
"sv.namehint", attr);
207class FileDescriptorInfo {
209 FileDescriptorInfo(StringAttr outputFileName, mlir::ValueRange substitutions)
210 : outputFileFormat(outputFileName), substitutions(substitutions) {
212 substitutions.empty() &&
213 "substitutions must be empty when output file name is empty");
216 FileDescriptorInfo() =
default;
219 bool isSubstitutionRequired()
const {
return !substitutions.empty(); }
222 bool isDefaultFd()
const {
return !outputFileFormat; }
224 StringAttr getOutputFileFormat()
const {
return outputFileFormat; }
225 mlir::ValueRange getSubstitutions()
const {
return substitutions; }
229 StringAttr outputFileFormat = {};
232 mlir::ValueRange substitutions;
242struct FIRRTLModuleLowering;
245struct CircuitLoweringState {
247 std::atomic<bool> usedPrintf{
false};
248 std::atomic<bool> usedAssertVerboseCond{
false};
249 std::atomic<bool> usedStopCond{
false};
250 std::atomic<bool> usedFileDescriptorLib{
false};
252 CircuitLoweringState(CircuitOp circuitOp,
bool enableAnnotationWarning,
255 : circuitOp(circuitOp), instanceGraph(instanceGraph),
256 enableAnnotationWarning(enableAnnotationWarning),
257 verificationFlavor(verificationFlavor), nlaTable(nlaTable) {
258 auto *
context = circuitOp.getContext();
262 AnnotationSet(circuitOp).getAnnotation(testBenchDirAnnoClass)) {
263 auto dirName = tbAnno.getMember<StringAttr>(
"dirname");
264 testBenchDirectory = hw::OutputFileAttr::getAsDirectory(
265 context, dirName.getValue(),
false,
true);
269 if (
auto module = dyn_cast<FModuleLike>(op))
280 testHarness =
nullptr;
281 }
else if (dut == testHarness) {
282 testHarness =
nullptr;
287 auto inDUT = [&](igraph::ModuleOpInterface child) {
289 if (
auto inst = instRec->getInstance<InstanceOp>())
290 return inst.getLowerToBind() || inst.getDoNotPrint();
293 if (
auto parent = dyn_cast<igraph::ModuleOpInterface>(*dut))
294 return getInstanceGraph().isAncestor(child, parent, isPhony);
297 circuitOp->walk([&](FModuleLike moduleOp) {
299 dutModules.insert(moduleOp);
303 Operation *getNewModule(Operation *oldModule) {
304 auto it = oldToNewModuleMap.find(oldModule);
305 return it != oldToNewModuleMap.end() ? it->second :
nullptr;
308 Operation *getOldModule(Operation *newModule) {
309 auto it = newToOldModuleMap.find(newModule);
310 return it != newToOldModuleMap.end() ? it->second :
nullptr;
313 void recordModuleMapping(Operation *oldFMod, Operation *newHWMod) {
314 oldToNewModuleMap[oldFMod] = newHWMod;
315 newToOldModuleMap[newHWMod] = oldFMod;
320 void processRemainingAnnotations(Operation *op,
const AnnotationSet &annoSet);
326 void addBind(sv::BindOp op) {
327 std::lock_guard<std::mutex> lock(bindsMutex);
333 hw::TypeAliasType getTypeAlias(Type rawType, BaseTypeAliasType firAliasType,
336 auto hwAlias = typeAliases.getTypedecl(firAliasType);
339 assert(!typeAliases.isFrozen() &&
340 "type aliases cannot be generated after its frozen");
341 return typeAliases.addTypedecl(rawType, firAliasType, typeLoc);
344 FModuleLike getDut() {
return dut; }
345 FModuleLike getTestHarness() {
return testHarness; }
351 bool isInDUT(igraph::ModuleOpInterface child) {
352 if (
auto hwModule = dyn_cast<hw::HWModuleOp>(child.getOperation()))
353 child = cast<igraph::ModuleOpInterface>(getOldModule(hwModule));
354 return dutModules.contains(child);
357 hw::OutputFileAttr getTestBenchDirectory() {
return testBenchDirectory; }
362 bool isInTestHarness(igraph::ModuleOpInterface mod) {
return !isInDUT(mod); }
369 Type
lowerType(Type type, Location loc) {
370 return ::lowerType(type, loc,
371 [&](Type rawType, BaseTypeAliasType firrtlType,
372 Location typeLoc) -> hw::TypeAliasType {
373 return getTypeAlias(rawType, firrtlType, typeLoc);
379 llvm::sys::SmartScopedLock<true> lock(verbatimSourcesMutex);
380 auto it = verbatimSourcesByFileName.find(fileName);
381 return it != verbatimSourcesByFileName.end() ? it->second :
nullptr;
386 void registerVerbatimSource(StringRef fileName,
388 llvm::sys::SmartScopedLock<true> lock(verbatimSourcesMutex);
389 verbatimSourcesByFileName[fileName] = verbatimOp;
393 emit::FileOp getEmitFileForFile(StringRef fileName) {
394 llvm::sys::SmartScopedLock<true> lock(emitFilesMutex);
395 auto it = emitFilesByFileName.find(fileName);
396 return it != emitFilesByFileName.end() ? it->second :
nullptr;
401 void registerEmitFile(StringRef fileName, emit::FileOp fileOp) {
402 llvm::sys::SmartScopedLock<true> lock(emitFilesMutex);
403 emitFilesByFileName[fileName] = fileOp;
407 friend struct FIRRTLModuleLowering;
408 friend struct FIRRTLLowering;
409 CircuitLoweringState(
const CircuitLoweringState &) =
delete;
410 void operator=(
const CircuitLoweringState &) =
delete;
413 DenseMap<Operation *, Operation *> oldToNewModuleMap;
416 DenseMap<Operation *, Operation *> newToOldModuleMap;
427 DenseSet<igraph::ModuleOpInterface> dutModules;
431 StringSet<> pendingAnnotations;
432 const bool enableAnnotationWarning;
433 std::mutex annotationPrintingMtx;
439 SmallVector<sv::BindOp> binds;
442 std::mutex bindsMutex;
450 FModuleLike testHarness;
453 hw::OutputFileAttr testBenchDirectory;
457 DenseMap<std::pair<Attribute, Attribute>, Attribute> instanceForceNames;
460 SetVector<StringAttr> macroDeclNames;
461 std::mutex macroDeclMutex;
463 void addMacroDecl(StringAttr name) {
464 std::unique_lock<std::mutex> lock(macroDeclMutex);
465 macroDeclNames.insert(name);
470 DenseMap<hw::HWModuleOp, SetVector<Attribute>> fragments;
471 llvm::sys::SmartMutex<true> fragmentsMutex;
474 llvm::sys::SmartScopedLock<true> lock(fragmentsMutex);
475 fragments[module].insert(
476 FlatSymbolRefAttr::get(circuitOp.getContext(), fragment));
491 struct RecordTypeAlias {
493 RecordTypeAlias(CircuitOp c) : circuitOp(c) {}
495 hw::TypeAliasType getTypedecl(BaseTypeAliasType firAlias)
const {
496 auto iter = firrtlTypeToAliasTypeMap.find(firAlias);
497 if (iter != firrtlTypeToAliasTypeMap.end())
502 bool isFrozen() {
return frozen; }
504 void freeze() { frozen =
true; }
506 hw::TypeAliasType addTypedecl(Type rawType, BaseTypeAliasType firAlias,
508 assert(!frozen &&
"Record already frozen, cannot be updated");
511 auto b = ImplicitLocOpBuilder::atBlockBegin(
513 &circuitOp->getParentRegion()->getBlocks().back());
515 b,
b.getStringAttr(circuitOp.getName() +
"__TYPESCOPE_"));
516 typeScope.getBodyRegion().push_back(
new Block());
518 auto typeName = firAlias.getName();
523 StringAttr::get(typeName.getContext(),
524 typeDeclNamespace.newName(typeName.getValue()));
526 auto typeScopeBuilder =
527 ImplicitLocOpBuilder::atBlockEnd(typeLoc, typeScope.getBodyBlock());
529 typeName, rawType,
nullptr);
530 auto hwAlias = hw::TypeAliasType::get(
531 SymbolRefAttr::get(typeScope.getSymNameAttr(),
532 {FlatSymbolRefAttr::get(typeDecl)}),
534 auto insert = firrtlTypeToAliasTypeMap.try_emplace(firAlias, hwAlias);
535 assert(insert.second &&
"Entry already exists, insert failed");
536 return insert.first->second;
545 DenseMap<Type, hw::TypeAliasType> firrtlTypeToAliasTypeMap;
553 RecordTypeAlias typeAliases = RecordTypeAlias(circuitOp);
556 llvm::StringMap<sv::SVVerbatimSourceOp> verbatimSourcesByFileName;
557 llvm::sys::SmartMutex<true> verbatimSourcesMutex;
560 llvm::StringMap<emit::FileOp> emitFilesByFileName;
561 llvm::sys::SmartMutex<true> emitFilesMutex;
564void CircuitLoweringState::processRemainingAnnotations(
566 if (!enableAnnotationWarning || annoSet.
empty())
568 std::lock_guard<std::mutex> lock(annotationPrintingMtx);
570 for (
auto a : annoSet) {
571 auto inserted = pendingAnnotations.insert(
a.getClass());
572 if (!inserted.second)
593 markDUTAnnoClass, metadataDirAnnoClass, testBenchDirAnnoClass,
598 extractGrandCentralAnnoClass,
601 extractAssertionsAnnoClass, extractAssumptionsAnnoClass,
602 extractCoverageAnnoClass,
606 moduleHierarchyAnnoClass, testHarnessHierarchyAnnoClass,
607 blackBoxTargetDirAnnoClass))
610 mlir::emitWarning(op->getLoc(),
"unprocessed annotation:'" +
a.getClass() +
611 "' still remaining after LowerToHW");
617struct FIRRTLModuleLowering
618 :
public circt::impl::LowerFIRRTLToHWBase<FIRRTLModuleLowering> {
620 void runOnOperation()
override;
621 void setEnableAnnotationWarning() { enableAnnotationWarning =
true; }
623 using LowerFIRRTLToHWBase<FIRRTLModuleLowering>::verificationFlavor;
626 void lowerFileHeader(CircuitOp op, CircuitLoweringState &
loweringState);
627 LogicalResult lowerPorts(ArrayRef<PortInfo> firrtlPorts,
628 SmallVectorImpl<hw::PortInfo> &ports,
629 Operation *moduleOp, StringRef moduleName,
631 bool handleForceNameAnnos(FModuleLike oldModule,
AnnotationSet &annos,
633 hw::HWModuleOp lowerModule(FModuleOp oldModule, Block *topLevelModule,
636 getVerbatimSourceForExtModule(FExtModuleOp oldModule, Block *topLevelModule,
638 hw::HWModuleLike lowerExtModule(FExtModuleOp oldModule, Block *topLevelModule,
641 lowerVerbatimExtModule(FExtModuleOp oldModule, Block *topLevelModule,
644 Block *topLevelModule,
648 lowerModulePortsAndMoveBody(FModuleOp oldModule,
hw::HWModuleOp newModule,
652 LogicalResult lowerFormalBody(verif::FormalOp formalOp,
654 LogicalResult lowerSimulationBody(verif::SimulationOp simulationOp,
656 LogicalResult lowerFileBody(emit::FileOp op);
664 bool enableAnnotationWarning,
666 auto pass = std::make_unique<FIRRTLModuleLowering>();
667 if (enableAnnotationWarning)
668 pass->setEnableAnnotationWarning();
669 pass->verificationFlavor = verificationFlavor;
675void FIRRTLModuleLowering::runOnOperation() {
679 auto *topLevelModule = getOperation().getBody();
683 for (
auto &op : *topLevelModule) {
684 if ((circuit = dyn_cast<CircuitOp>(&op)))
691 auto *circuitBody = circuit.getBodyBlock();
695 CircuitLoweringState state(circuit, enableAnnotationWarning,
696 verificationFlavor, getAnalysis<InstanceGraph>(),
697 &getAnalysis<NLATable>());
699 SmallVector<Operation *, 32> opsToProcess;
702 moveVerifAnno(getOperation(), circuitAnno, extractAssertionsAnnoClass,
703 "firrtl.extract.assert");
704 moveVerifAnno(getOperation(), circuitAnno, extractAssumptionsAnnoClass,
705 "firrtl.extract.assume");
706 moveVerifAnno(getOperation(), circuitAnno, extractCoverageAnnoClass,
707 "firrtl.extract.cover");
708 circuitAnno.removeAnnotationsWithClass(extractAssertionsAnnoClass,
709 extractAssumptionsAnnoClass,
710 extractCoverageAnnoClass);
712 state.processRemainingAnnotations(circuit, circuitAnno);
715 for (
auto &op : make_early_inc_range(circuitBody->getOperations())) {
717 TypeSwitch<Operation *, LogicalResult>(&op)
718 .Case<FModuleOp>([&](
auto module) {
719 auto loweredMod = lowerModule(module, topLevelModule, state);
723 state.recordModuleMapping(&op, loweredMod);
724 opsToProcess.push_back(loweredMod);
726 module.walk([&](Operation *op) {
727 for (auto res : op->getResults()) {
729 type_dyn_cast<BaseTypeAliasType>(res.getType()))
730 state.lowerType(aliasType, op->getLoc());
733 return lowerModulePortsAndMoveBody(module, loweredMod, state);
735 .Case<FExtModuleOp>([&](
auto extModule) {
737 lowerExtModule(extModule, topLevelModule, state);
740 state.recordModuleMapping(&op, loweredMod);
743 .Case<FMemModuleOp>([&](
auto memModule) {
745 lowerMemModule(memModule, topLevelModule, state);
748 state.recordModuleMapping(&op, loweredMod);
751 .Case<FormalOp>([&](
auto oldOp) {
752 auto builder = OpBuilder::atBlockEnd(topLevelModule);
753 auto newOp = verif::FormalOp::create(builder, oldOp.getLoc(),
755 oldOp.getParametersAttr());
756 newOp.getBody().emplaceBlock();
757 state.recordModuleMapping(oldOp, newOp);
758 opsToProcess.push_back(newOp);
761 .Case<SimulationOp>([&](
auto oldOp) {
762 auto loc = oldOp.getLoc();
763 auto builder = OpBuilder::atBlockEnd(topLevelModule);
764 auto newOp = verif::SimulationOp::create(
765 builder, loc, oldOp.getNameAttr(), oldOp.getParametersAttr());
766 auto &body = newOp.getRegion().emplaceBlock();
767 body.addArgument(seq::ClockType::get(builder.getContext()), loc);
768 body.addArgument(builder.getI1Type(), loc);
769 state.recordModuleMapping(oldOp, newOp);
770 opsToProcess.push_back(newOp);
773 .Case<emit::FileOp>([&](
auto fileOp) {
774 fileOp->moveBefore(topLevelModule, topLevelModule->end());
775 opsToProcess.push_back(fileOp);
778 .Case<OptionOp, OptionCaseOp>([&](
auto) {
782 .Default([&](Operation *op) {
787 op->moveBefore(topLevelModule, topLevelModule->end());
793 return signalPassFailure();
796 state.typeAliases.freeze();
801 SmallVector<Attribute> dutHierarchyFiles;
802 SmallVector<Attribute> testHarnessHierarchyFiles;
803 circuitAnno.removeAnnotations([&](
Annotation annotation) {
804 if (annotation.
isClass(moduleHierarchyAnnoClass)) {
805 auto file = hw::OutputFileAttr::getFromFilename(
807 annotation.
getMember<StringAttr>(
"filename").getValue(),
809 dutHierarchyFiles.push_back(file);
812 if (annotation.
isClass(testHarnessHierarchyAnnoClass)) {
813 auto file = hw::OutputFileAttr::getFromFilename(
815 annotation.
getMember<StringAttr>(
"filename").getValue(),
819 if (state.getTestHarness())
820 testHarnessHierarchyFiles.push_back(file);
822 dutHierarchyFiles.push_back(file);
828 if (!dutHierarchyFiles.empty())
829 state.getNewModule(state.getDut())
831 ArrayAttr::get(&getContext(), dutHierarchyFiles));
832 if (!testHarnessHierarchyFiles.empty())
833 state.getNewModule(state.getTestHarness())
835 ArrayAttr::get(&getContext(), testHarnessHierarchyFiles));
839 mlir::failableParallelForEach(&getContext(), opsToProcess, [&](
auto op) {
843 return signalPassFailure();
846 for (
auto bind : state.binds) {
851 for (
auto &[module, fragments] : state.fragments)
853 ArrayAttr::
get(&getContext(), fragments.getArrayRef()));
856 for (
auto oldNew : state.oldToNewModuleMap)
857 oldNew.first->erase();
859 if (!state.macroDeclNames.empty()) {
860 ImplicitLocOpBuilder
b(UnknownLoc::get(&getContext()), circuit);
861 for (
auto name : state.macroDeclNames) {
862 sv::MacroDeclOp::create(b, name);
867 lowerFileHeader(circuit, state);
874void FIRRTLModuleLowering::lowerFileHeader(CircuitOp op,
875 CircuitLoweringState &state) {
878 ImplicitLocOpBuilder
b(UnknownLoc::get(&getContext()), op);
882 auto emitGuardedDefine = [&](StringRef guard, StringRef defName,
883 StringRef defineTrue =
"",
884 StringRef defineFalse = StringRef()) {
885 if (!defineFalse.data()) {
886 assert(defineTrue.data() &&
"didn't define anything");
888 b, guard, [&]() { sv::MacroDefOp::create(b, defName, defineTrue); });
893 if (defineTrue.data())
894 sv::MacroDefOp::create(b, defName, defineTrue);
896 [&]() { sv::MacroDefOp::create(b, defName, defineFalse); });
901 auto emitGuard = [&](
const char *guard, llvm::function_ref<void(
void)> body) {
903 b, guard, [] {}, body);
906 if (state.usedFileDescriptorLib) {
908 SmallVector<hw::ModulePort> ports;
912 namePort.
name =
b.getStringAttr(
"name");
913 namePort.
type = hw::StringType::get(
b.getContext());
914 namePort.
dir = hw::ModulePort::Direction::Input;
915 ports.push_back(namePort);
919 fdPort.
name =
b.getStringAttr(
"fd");
920 fdPort.
type =
b.getIntegerType(32);
921 fdPort.
dir = hw::ModulePort::Direction::Output;
922 ports.push_back(fdPort);
925 auto moduleType = hw::ModuleType::get(
b.getContext(), ports);
927 SmallVector<NamedAttribute> perArgumentsAttr;
928 perArgumentsAttr.push_back(
929 {sv::FuncOp::getExplicitlyReturnedAttrName(),
b.getUnitAttr()});
931 SmallVector<Attribute> argumentAttr = {
932 DictionaryAttr::get(
b.getContext(), {}),
933 DictionaryAttr::get(
b.getContext(), perArgumentsAttr)};
936 auto func = sv::FuncOp::create(
938 "__circt_lib_logging::FileDescriptor::get", moduleType,
941 {b.getDictionaryAttr({}),
b.getDictionaryAttr(perArgumentsAttr)}),
947 b.getStringAttr(
"__circt_lib_logging::FileDescriptor::get"));
950 sv::MacroDeclOp::create(b,
"__CIRCT_LIB_LOGGING");
952 emit::FragmentOp::create(b,
"CIRCT_LIB_LOGGING_FRAGMENT", [&] {
953 emitGuard(
"SYNTHESIS", [&]() {
954 emitGuard(
"__CIRCT_LIB_LOGGING", [&]() {
955 sv::VerbatimOp::create(b, R
"(// CIRCT Logging Library
956package __circt_lib_logging;
957 class FileDescriptor;
958 static int global_id [string];
959 static function int get(string name);
960 if (global_id.exists(name) == 32'h0) begin
961 global_id[name] = $fopen(name, "w");
962 if (global_id[name] == 32'h0)
963 $error("Failed to open file %s", name);
965 return global_id[name];
971 sv::MacroDefOp::create(b, "__CIRCT_LIB_LOGGING",
"");
977 if (state.usedPrintf) {
978 sv::MacroDeclOp::create(b,
"PRINTF_COND");
979 sv::MacroDeclOp::create(b,
"PRINTF_COND_");
980 emit::FragmentOp::create(b,
"PRINTF_COND_FRAGMENT", [&] {
981 sv::VerbatimOp::create(
982 b,
"\n// Users can define 'PRINTF_COND' to add an extra gate to "
984 emitGuard(
"PRINTF_COND_", [&]() {
985 emitGuardedDefine(
"PRINTF_COND",
"PRINTF_COND_",
"(`PRINTF_COND)",
"1");
990 if (state.usedAssertVerboseCond) {
991 sv::MacroDeclOp::create(b,
"ASSERT_VERBOSE_COND");
992 sv::MacroDeclOp::create(b,
"ASSERT_VERBOSE_COND_");
993 emit::FragmentOp::create(b,
"ASSERT_VERBOSE_COND_FRAGMENT", [&] {
994 sv::VerbatimOp::create(
995 b,
"\n// Users can define 'ASSERT_VERBOSE_COND' to add an extra "
996 "gate to assert error printing.");
997 emitGuard(
"ASSERT_VERBOSE_COND_", [&]() {
998 emitGuardedDefine(
"ASSERT_VERBOSE_COND",
"ASSERT_VERBOSE_COND_",
999 "(`ASSERT_VERBOSE_COND)",
"1");
1004 if (state.usedStopCond) {
1005 sv::MacroDeclOp::create(b,
"STOP_COND");
1006 sv::MacroDeclOp::create(b,
"STOP_COND_");
1007 emit::FragmentOp::create(b,
"STOP_COND_FRAGMENT", [&] {
1008 sv::VerbatimOp::create(
1009 b,
"\n// Users can define 'STOP_COND' to add an extra gate "
1010 "to stop conditions.");
1011 emitGuard(
"STOP_COND_", [&]() {
1012 emitGuardedDefine(
"STOP_COND",
"STOP_COND_",
"(`STOP_COND)",
"1");
1019FIRRTLModuleLowering::lowerPorts(ArrayRef<PortInfo> firrtlPorts,
1020 SmallVectorImpl<hw::PortInfo> &ports,
1021 Operation *moduleOp, StringRef moduleName,
1023 ports.reserve(firrtlPorts.size());
1025 size_t numResults = 0;
1026 for (
auto e :
llvm::enumerate(firrtlPorts)) {
1028 size_t portNo = e.index();
1033 if (firrtlPort.
sym.size() > 1 ||
1034 (firrtlPort.
sym.size() == 1 && !firrtlPort.
sym.getSymName()))
1035 return emitError(firrtlPort.
loc)
1036 <<
"cannot lower aggregate port " << firrtlPort.
name
1037 <<
" with field sensitive symbols, HW dialect does not support "
1038 "per field symbols yet.";
1039 hwPort.
setSym(firrtlPort.
sym, moduleOp->getContext());
1041 if (hadDontTouch && !hwPort.
getSym()) {
1042 if (hwPort.
type.isInteger(0)) {
1043 if (enableAnnotationWarning) {
1044 mlir::emitWarning(firrtlPort.
loc)
1045 <<
"zero width port " << hwPort.
name
1046 <<
" has dontTouch annotation, removing anyway";
1052 hw::InnerSymAttr::get(StringAttr::get(
1053 moduleOp->getContext(),
1054 Twine(
"__") + moduleName + Twine(
"__DONTTOUCH__") +
1055 Twine(portNo) + Twine(
"__") + firrtlPort.
name.strref())),
1056 moduleOp->getContext());
1061 moduleOp->emitError(
"cannot lower this port type to HW");
1067 if (hwPort.
type.isInteger(0)) {
1068 auto sym = hwPort.
getSym();
1069 if (sym && !sym.empty()) {
1070 return mlir::emitError(firrtlPort.
loc)
1071 <<
"zero width port " << hwPort.
name
1072 <<
" is referenced by name [" << sym
1073 <<
"] (e.g. in an XMR) but must be removed";
1080 hwPort.
dir = hw::ModulePort::Direction::Output;
1081 hwPort.
argNum = numResults++;
1082 }
else if (firrtlPort.
isInput()) {
1083 hwPort.
dir = hw::ModulePort::Direction::Input;
1084 hwPort.
argNum = numArgs++;
1088 hwPort.
type = hw::InOutType::get(hwPort.
type);
1089 hwPort.
dir = hw::ModulePort::Direction::InOut;
1090 hwPort.
argNum = numArgs++;
1092 hwPort.
loc = firrtlPort.
loc;
1093 ports.push_back(hwPort);
1103 auto params = llvm::map_range(module.getParameters(), [](Attribute a) {
1104 return cast<ParamDeclAttr>(a);
1109 Builder builder(module);
1114 SmallVector<Attribute> newParams;
1115 for (
const ParamDeclAttr &entry : params) {
1116 auto name = entry.getName();
1117 auto type = entry.getType();
1118 auto value = ignoreValues ? Attribute() : entry.getValue();
1120 hw::ParamDeclAttr::get(builder.getContext(), name, type, value);
1121 newParams.push_back(paramAttr);
1123 return builder.getArrayAttr(newParams);
1126bool FIRRTLModuleLowering::handleForceNameAnnos(
1129 bool failed =
false;
1132 if (!anno.
isClass(forceNameAnnoClass))
1135 auto sym = anno.
getMember<FlatSymbolRefAttr>(
"circt.nonlocal");
1142 auto diag = oldModule.emitOpError()
1143 <<
"contains a '" << forceNameAnnoClass
1144 <<
"' that is not a non-local annotation";
1145 diag.attachNote() <<
"the erroneous annotation is '" << anno.
getDict()
1156 auto diag = oldModule.emitOpError()
1157 <<
"contains a '" << forceNameAnnoClass
1158 <<
"' whose non-local symbol, '" << sym
1159 <<
"' does not exist in the circuit";
1160 diag.attachNote() <<
"the erroneous annotation is '" << anno.
getDict();
1173 cast<hw::InnerRefAttr>(nla.getNamepath().getValue().take_back(2)[0]);
1175 {{inst.getModule(), inst.getName()}, anno.
getMember(
"name")});
1176 if (!inserted.second &&
1177 (anno.
getMember(
"name") != (inserted.first->second))) {
1178 auto diag = oldModule.emitError()
1179 <<
"contained multiple '" << forceNameAnnoClass
1180 <<
"' with different names: " << inserted.first->second
1181 <<
" was not " << anno.
getMember(
"name");
1182 diag.attachNote() <<
"the erroneous annotation is '" << anno.
getDict()
1193 FExtModuleOp oldModule, Block *topLevelModule,
1204 SmallVector<PortInfo> firrtlPorts = oldModule.getPorts();
1205 SmallVector<hw::PortInfo, 8> ports;
1206 if (failed(lowerPorts(firrtlPorts, ports, oldModule, oldModule.getName(),
1211 StringRef verilogName;
1212 if (
auto defName = oldModule.getDefname())
1213 verilogName = defName.value();
1215 verilogName = oldModule.getName();
1217 auto builder = OpBuilder::atBlockEnd(topLevelModule);
1219 auto filesAttr = verbatimAnno.
getMember<ArrayAttr>(
"files");
1220 if (!filesAttr || filesAttr.empty()) {
1221 oldModule->emitError(
"VerbatimBlackBoxAnno missing or empty files array");
1226 auto primaryFile = cast<DictionaryAttr>(filesAttr[0]);
1227 auto primaryFileContent = primaryFile.getAs<StringAttr>(
"content");
1228 auto primaryOutputFile = primaryFile.getAs<StringAttr>(
"output_file");
1230 if (!primaryFileContent || !primaryOutputFile) {
1231 oldModule->emitError(
"VerbatimBlackBoxAnno file missing fields");
1235 auto primaryOutputFileAttr = hw::OutputFileAttr::getFromFilename(
1236 builder.getContext(), primaryOutputFile.getValue());
1238 auto primaryFileName = llvm::sys::path::filename(primaryOutputFile);
1239 auto verbatimSource =
loweringState.getVerbatimSourceForFile(primaryFileName);
1242 SmallVector<Attribute> additionalFiles;
1246 for (
size_t i = 1; i < filesAttr.size(); ++i) {
1247 auto file = cast<DictionaryAttr>(filesAttr[i]);
1248 auto content = file.getAs<StringAttr>(
"content");
1249 auto outputFile = file.getAs<StringAttr>(
"output_file");
1250 auto fileName = llvm::sys::path::filename(outputFile);
1252 if (!(content && outputFile)) {
1253 oldModule->emitError(
"VerbatimBlackBoxAnno file missing fields");
1261 auto fileSymbolName = circuitNamespace.newName(fileName);
1262 emitFile = emit::FileOp::create(builder, oldModule.getLoc(),
1263 outputFile.getValue(), fileSymbolName);
1264 builder.setInsertionPointToStart(&
emitFile.getBodyRegion().front());
1265 emit::VerbatimOp::create(builder, oldModule.getLoc(), content);
1266 builder.setInsertionPointAfter(
emitFile);
1269 auto ext = llvm::sys::path::extension(outputFile.getValue());
1270 bool excludeFromFileList = (ext ==
".h" || ext ==
".vh" || ext ==
".svh");
1271 auto outputFileAttr = hw::OutputFileAttr::getFromFilename(
1272 builder.getContext(), outputFile.getValue(), excludeFromFileList);
1273 emitFile->setAttr(
"output_file", outputFileAttr);
1277 additionalFiles.push_back(FlatSymbolRefAttr::get(
emitFile));
1283 parameters = builder.getArrayAttr({});
1285 if (!verbatimSource) {
1286 verbatimSource = sv::SVVerbatimSourceOp::create(
1287 builder, oldModule.getLoc(),
1288 circuitNamespace.newName(primaryFileName.str()),
1289 primaryFileContent.getValue(), primaryOutputFileAttr, parameters,
1290 additionalFiles.empty() ?
nullptr
1291 : builder.getArrayAttr(additionalFiles),
1292 builder.getStringAttr(verilogName));
1294 SymbolTable::setSymbolVisibility(
1295 verbatimSource, SymbolTable::getSymbolVisibility(oldModule));
1297 loweringState.registerVerbatimSource(primaryFileName, verbatimSource);
1300 return verbatimSource;
1304FIRRTLModuleLowering::lowerExtModule(FExtModuleOp oldModule,
1305 Block *topLevelModule,
1307 if (
auto verbatimMod =
1308 lowerVerbatimExtModule(oldModule, topLevelModule,
loweringState))
1314 SmallVector<PortInfo> firrtlPorts = oldModule.getPorts();
1315 SmallVector<hw::PortInfo, 8> ports;
1316 if (failed(lowerPorts(firrtlPorts, ports, oldModule, oldModule.getName(),
1320 StringRef verilogName;
1321 if (
auto defName = oldModule.getDefname())
1322 verilogName = defName.value();
1325 auto builder = OpBuilder::atBlockEnd(topLevelModule);
1326 auto nameAttr = builder.getStringAttr(oldModule.getName());
1331 auto newModule = hw::HWModuleExternOp::create(
1332 builder, oldModule.getLoc(), nameAttr, ports, verilogName, parameters);
1333 SymbolTable::setSymbolVisibility(newModule,
1334 SymbolTable::getSymbolVisibility(oldModule));
1336 bool hasOutputPort =
1337 llvm::any_of(firrtlPorts, [&](
auto p) {
return p.isOutput(); });
1338 if (!hasOutputPort &&
1340 internalVerifBlackBoxAnnoClass) &&
1342 newModule->setAttr(
"firrtl.extract.cover.extra", builder.getUnitAttr());
1345 if (
auto extReqs = oldModule.getExternalRequirements();
1346 extReqs && !extReqs.empty())
1347 newModule->setAttr(
"circt.external_requirements", extReqs);
1352 loweringState.processRemainingAnnotations(oldModule, annos);
1357 FExtModuleOp oldModule, Block *topLevelModule,
1362 auto verbatimSource =
1363 getVerbatimSourceForExtModule(oldModule, topLevelModule,
loweringState);
1365 if (!verbatimSource)
1368 SmallVector<PortInfo> firrtlPorts = oldModule.getPorts();
1369 SmallVector<hw::PortInfo, 8> ports;
1370 if (failed(lowerPorts(firrtlPorts, ports, oldModule, oldModule.getName(),
1374 StringRef verilogName;
1375 if (
auto defName = oldModule.getDefname())
1376 verilogName = defName.value();
1378 auto builder = OpBuilder::atBlockEnd(topLevelModule);
1380 auto newModule = sv::SVVerbatimModuleOp::create(
1383 builder.getStringAttr(oldModule.getName()),
1385 FlatSymbolRefAttr::get(verbatimSource),
1386 parameters ? parameters : builder.getArrayAttr({}),
1387 verilogName.empty() ? StringAttr{}
1388 : builder.getStringAttr(verilogName));
1390 SymbolTable::setSymbolVisibility(newModule,
1391 SymbolTable::getSymbolVisibility(oldModule));
1393 bool hasOutputPort =
1394 llvm::any_of(firrtlPorts, [&](
auto p) {
return p.isOutput(); });
1395 if (!hasOutputPort &&
1397 internalVerifBlackBoxAnnoClass) &&
1399 newModule->setAttr(
"firrtl.extract.cover.extra", builder.getUnitAttr());
1402 if (
auto extReqs = oldModule.getExternalRequirements();
1403 extReqs && !extReqs.empty())
1404 newModule->setAttr(
"circt.external_requirements", extReqs);
1409 loweringState.processRemainingAnnotations(oldModule, annos);
1414FIRRTLModuleLowering::lowerMemModule(FMemModuleOp oldModule,
1415 Block *topLevelModule,
1418 SmallVector<PortInfo> firrtlPorts = oldModule.getPorts();
1419 SmallVector<hw::PortInfo, 8> ports;
1420 if (failed(lowerPorts(firrtlPorts, ports, oldModule, oldModule.getName(),
1425 auto builder = OpBuilder::atBlockEnd(topLevelModule);
1426 auto newModule = hw::HWModuleExternOp::create(
1427 builder, oldModule.getLoc(), oldModule.getModuleNameAttr(), ports,
1428 oldModule.getModuleNameAttr());
1436FIRRTLModuleLowering::lowerModule(FModuleOp oldModule, Block *topLevelModule,
1439 SmallVector<PortInfo> firrtlPorts = oldModule.getPorts();
1440 SmallVector<hw::PortInfo, 8> ports;
1441 if (failed(lowerPorts(firrtlPorts, ports, oldModule, oldModule.getName(),
1446 auto builder = OpBuilder::atBlockEnd(topLevelModule);
1447 auto nameAttr = builder.getStringAttr(oldModule.getName());
1449 hw::HWModuleOp::create(builder, oldModule.getLoc(), nameAttr, ports);
1451 if (
auto comment = oldModule->getAttrOfType<StringAttr>(
"comment"))
1452 newModule.setCommentAttr(comment);
1455 SmallVector<StringRef, 13> attrNames = {
1456 "annotations",
"convention",
"layers",
1457 "portNames",
"sym_name",
"portDirections",
1458 "portTypes",
"portAnnotations",
"portSymbols",
1459 "portLocations",
"parameters", SymbolTable::getVisibilityAttrName(),
1462 DenseSet<StringRef> attrSet(attrNames.begin(), attrNames.end());
1463 SmallVector<NamedAttribute> newAttrs(newModule->getAttrs());
1465 llvm::make_filter_range(oldModule->getAttrs(), [&](auto namedAttr) {
1466 return !attrSet.count(namedAttr.getName()) &&
1467 !newModule->getAttrDictionary().contains(namedAttr.getName());
1469 newAttrs.push_back(i);
1471 newModule->setAttrs(newAttrs);
1475 SymbolTable::setSymbolVisibility(newModule,
1476 SymbolTable::getSymbolVisibility(oldModule));
1482 newModule->setAttr(
"firrtl.extract.cover.extra", builder.getUnitAttr());
1486 if (
auto testBenchDir =
loweringState.getTestBenchDirectory())
1488 if (!newModule->hasAttr(
"output_file"))
1489 newModule->setAttr(
"output_file", testBenchDir);
1490 newModule->setAttr(
"firrtl.extract.do_not_extract",
1491 builder.getUnitAttr());
1492 newModule.setCommentAttr(
1493 builder.getStringAttr(
"VCS coverage exclude_file"));
1499 loweringState.processRemainingAnnotations(oldModule, annos);
1508 Operation *insertPoint) {
1509 if (!value.hasOneUse())
1512 auto attach = dyn_cast<AttachOp>(*value.user_begin());
1513 if (!attach || attach.getNumOperands() != 2)
1517 auto loweredType =
lowerType(value.getType());
1518 if (loweredType.isInteger(0))
1523 auto attachedValue = attach.getOperand(attach.getOperand(0) == value);
1524 auto *op = attachedValue.getDefiningOp();
1525 if (op && op->getBlock() == insertPoint->getBlock() &&
1526 !op->isBeforeInBlock(insertPoint))
1531 ImplicitLocOpBuilder builder(insertPoint->getLoc(), insertPoint);
1547 if (type_isa<AnalogType>(flipValue.getType()))
1550 Operation *connectOp =
nullptr;
1551 for (
auto &use : flipValue.getUses()) {
1554 if (use.getOperandNumber() != 0)
1556 if (!isa<ConnectOp, MatchingConnectOp>(use.getOwner()))
1562 connectOp = use.getOwner();
1572 loweringState.lowerType(flipValue.getType(), flipValue.getLoc());
1573 if (loweredType.isInteger(0))
1578 ImplicitLocOpBuilder builder(insertPoint->getLoc(), insertPoint);
1580 auto connectSrc = connectOp->getOperand(1);
1583 if (!isa<FIRRTLType>(connectSrc.getType())) {
1589 if (!type_cast<FIRRTLBaseType>(connectSrc.getType()).isPassive())
1591 mlir::UnrealizedConversionCastOp::create(
1593 type_cast<FIRRTLBaseType>(connectSrc.getType()).getPassiveType(),
1599 auto destTy = type_cast<FIRRTLBaseType>(flipValue.getType()).getPassiveType();
1601 if (destTy != connectSrc.getType() &&
1602 (isa<BaseTypeAliasType>(connectSrc.getType()) ||
1603 isa<BaseTypeAliasType>(destTy))) {
1605 builder.createOrFold<BitCastOp>(flipValue.getType(), connectSrc);
1607 if (!destTy.isGround()) {
1609 if (destTy != type_cast<FIRRTLType>(connectSrc.getType()))
1611 }
else if (destTy.getBitWidthOrSentinel() !=
1612 type_cast<FIRRTLBaseType>(connectSrc.getType())
1613 .getBitWidthOrSentinel()) {
1616 auto destWidth = destTy.getBitWidthOrSentinel();
1617 assert(destWidth != -1 &&
"must know integer widths");
1618 connectSrc = builder.createOrFold<PadPrimOp>(destTy, connectSrc, destWidth);
1630 SmallVector<SubfieldOp> accesses;
1631 for (
auto *op : structValue.getUsers()) {
1632 assert(isa<SubfieldOp>(op));
1633 auto fieldAccess = cast<SubfieldOp>(op);
1635 fieldAccess.getInput().getType().base().getElementIndex(field);
1636 if (elemIndex && *elemIndex == fieldAccess.getFieldIndex())
1637 accesses.push_back(fieldAccess);
1645LogicalResult FIRRTLModuleLowering::lowerModulePortsAndMoveBody(
1648 ImplicitLocOpBuilder bodyBuilder(oldModule.getLoc(), newModule.getBody());
1655 bodyBuilder.setInsertionPoint(cursor);
1658 SmallVector<PortInfo> firrtlPorts = oldModule.getPorts();
1659 assert(oldModule.getBody().getNumArguments() == firrtlPorts.size() &&
1660 "port count mismatch");
1662 SmallVector<Value, 4> outputs;
1665 auto *outputOp = newModule.getBodyBlock()->getTerminator();
1666 ImplicitLocOpBuilder outputBuilder(oldModule.getLoc(), outputOp);
1668 unsigned nextHWInputArg = 0;
1669 int hwPortIndex = -1;
1670 for (
auto [firrtlPortIndex, port] :
llvm::enumerate(firrtlPorts)) {
1672 auto oldArg = oldModule.getBody().getArgument(firrtlPortIndex);
1675 type_isa<FIRRTLBaseType>(port.type) &&
1676 type_cast<FIRRTLBaseType>(port.type).getBitWidthOrSentinel() == 0;
1680 if (!port.isOutput() && !isZeroWidth) {
1683 Value newArg = newModule.getBody().getArgument(nextHWInputArg++);
1689 oldArg.replaceAllUsesWith(newArg);
1695 if (isZeroWidth && port.isInput()) {
1697 WireOp::create(bodyBuilder, port.type,
1698 "." + port.getName().str() +
".0width_input")
1700 oldArg.replaceAllUsesWith(newArg);
1708 outputs.push_back(value);
1709 assert(oldArg.use_empty() &&
"should have removed all uses of oldArg");
1715 auto newArg = WireOp::create(bodyBuilder, port.type,
1716 "." + port.getName().str() +
".output");
1719 oldArg.replaceAllUsesWith(newArg.getResult());
1722 auto resultHWType =
loweringState.lowerType(port.type, port.loc);
1723 if (!resultHWType.isInteger(0)) {
1726 outputs.push_back(output);
1729 if (
auto sym = newModule.getPort(hwPortIndex).getSym()) {
1730 newArg.setInnerSymAttr(sym);
1731 newModule.setPortSymbolAttr(hwPortIndex, {});
1737 outputOp->setOperands(outputs);
1740 auto &oldBlockInstList = oldModule.getBodyBlock()->getOperations();
1741 auto &newBlockInstList = newModule.getBodyBlock()->getOperations();
1742 newBlockInstList.splice(Block::iterator(cursor), oldBlockInstList,
1743 oldBlockInstList.begin(), oldBlockInstList.end());
1754FIRRTLModuleLowering::lowerFormalBody(verif::FormalOp newOp,
1756 auto builder = OpBuilder::atBlockEnd(&newOp.getBody().front());
1761 auto oldOp = cast<FormalOp>(
loweringState.getOldModule(newOp));
1762 auto moduleName = oldOp.getModuleNameAttr().getAttr();
1763 auto oldModule = cast<FModuleOp>(
1764 loweringState.getInstanceGraph().lookup(moduleName)->getModule());
1765 auto newModule = cast<hw::HWModuleOp>(
loweringState.getNewModule(oldModule));
1768 SmallVector<Value> symbolicInputs;
1769 for (
auto arg : newModule.getBody().getArguments())
1770 symbolicInputs.push_back(
1771 verif::SymbolicValueOp::create(builder, arg.
getLoc(), arg.getType()));
1774 hw::InstanceOp::create(builder, newOp.getLoc(), newModule,
1775 newModule.getNameAttr(), symbolicInputs);
1782FIRRTLModuleLowering::lowerSimulationBody(verif::SimulationOp newOp,
1784 auto builder = OpBuilder::atBlockEnd(newOp.getBody());
1787 auto oldOp = cast<SimulationOp>(
loweringState.getOldModule(newOp));
1788 auto moduleName = oldOp.getModuleNameAttr().getAttr();
1789 auto oldModule = cast<FModuleLike>(
1790 *
loweringState.getInstanceGraph().lookup(moduleName)->getModule());
1792 cast<hw::HWModuleLike>(
loweringState.getNewModule(oldModule));
1796 SmallVector<Value> inputs(newOp.getBody()->args_begin(),
1797 newOp.getBody()->args_end());
1798 auto instOp = hw::InstanceOp::create(builder, newOp.getLoc(), newModule,
1799 newModule.getNameAttr(), inputs);
1800 verif::YieldOp::create(builder, newOp.getLoc(), instOp.getResults());
1810struct FIRRTLLowering :
public FIRRTLVisitor<FIRRTLLowering, LogicalResult> {
1812 FIRRTLLowering(
hw::HWModuleOp module, CircuitLoweringState &circuitState)
1813 : theModule(module), circuitState(circuitState),
1814 builder(module.
getLoc(), module.getContext()), moduleNamespace(module),
1815 backedgeBuilder(builder, module.
getLoc()) {}
1817 LogicalResult
run();
1820 Value getOrCreateClockConstant(seq::ClockConst clock);
1821 Value getOrCreateIntConstant(
const APInt &value);
1822 Value getOrCreateIntConstant(
unsigned numBits, uint64_t val,
1823 bool isSigned =
false) {
1824 return getOrCreateIntConstant(APInt(numBits, val, isSigned));
1826 Attribute getOrCreateAggregateConstantAttribute(Attribute value, Type type);
1827 Value getOrCreateXConstant(
unsigned numBits);
1828 Value getOrCreateZConstant(Type type);
1829 Value getPossiblyInoutLoweredValue(Value value);
1830 Value getLoweredValue(Value value);
1831 Value getLoweredNonClockValue(Value value);
1832 Value getLoweredAndExtendedValue(Value value, Type destType);
1833 Value getLoweredAndExtOrTruncValue(Value value, Type destType);
1834 LogicalResult setLowering(Value orig, Value result);
1835 LogicalResult setPossiblyFoldedLowering(Value orig, Value result);
1836 template <
typename ResultOpType,
typename... CtorArgTypes>
1837 LogicalResult setLoweringTo(Operation *orig, CtorArgTypes... args);
1838 template <
typename ResultOpType,
typename... CtorArgTypes>
1839 LogicalResult setLoweringToLTL(Operation *orig, CtorArgTypes... args);
1840 Backedge createBackedge(Location loc, Type type);
1841 Backedge createBackedge(Value orig, Type type);
1842 bool updateIfBackedge(Value dest, Value src);
1845 bool requiresInnerSymbol(hw::InnerSymbolOpInterface op) {
1850 if (
auto forceable = dyn_cast<Forceable>(op.getOperation()))
1851 if (forceable.isForceable())
1859 hw::InnerSymAttr lowerInnerSymbol(hw::InnerSymbolOpInterface op) {
1860 auto attr = op.getInnerSymAttr();
1864 if (requiresInnerSymbol(op))
1866 op.getContext(), attr, 0,
1874 LogicalResult prepareInstanceOperands(ArrayRef<PortInfo> portInfo,
1875 Operation *instanceOp,
1876 SmallVectorImpl<Value> &inputOperands);
1878 void runWithInsertionPointAtEndOfBlock(
const std::function<
void(
void)> &fn,
1882 Value getReadValue(Value v);
1884 Value getNonClockValue(Value v);
1886 void addToAlwaysBlock(sv::EventControl clockEdge, Value clock,
1887 sv::ResetType resetStyle, sv::EventControl resetEdge,
1888 Value reset,
const std::function<
void(
void)> &body = {},
1889 const std::function<void(
void)> &resetBody = {});
1890 void addToAlwaysBlock(Value clock,
1891 const std::function<
void(
void)> &body = {}) {
1892 addToAlwaysBlock(sv::EventControl::AtPosEdge, clock, sv::ResetType(),
1893 sv::EventControl(), Value(), body,
1894 std::function<
void(
void)>());
1897 LogicalResult emitGuards(Location loc, ArrayRef<Attribute> guards,
1898 std::function<
void(
void)>
emit);
1899 void addToIfDefBlock(StringRef cond, std::function<
void(
void)> thenCtor,
1900 std::function<
void(
void)> elseCtor = {});
1901 void addToInitialBlock(std::function<
void(
void)> body);
1902 void addIfProceduralBlock(Value cond, std::function<
void(
void)> thenCtor,
1903 std::function<
void(
void)> elseCtor = {});
1904 Value getExtOrTruncAggregateValue(Value array,
FIRRTLBaseType sourceType,
1906 bool allowTruncate);
1907 Value createArrayIndexing(Value array, Value index);
1908 Value createValueWithMuxAnnotation(Operation *op,
bool isMux2);
1910 using FIRRTLVisitor<FIRRTLLowering, LogicalResult>::visitExpr;
1911 using FIRRTLVisitor<FIRRTLLowering, LogicalResult>::visitDecl;
1912 using FIRRTLVisitor<FIRRTLLowering, LogicalResult>::visitStmt;
1915 enum UnloweredOpResult { AlreadyLowered, NowLowered, LoweringFailure };
1916 UnloweredOpResult handleUnloweredOp(Operation *op);
1917 LogicalResult visitExpr(ConstantOp op);
1918 LogicalResult visitExpr(SpecialConstantOp op);
1919 LogicalResult visitExpr(SubindexOp op);
1920 LogicalResult visitExpr(SubaccessOp op);
1921 LogicalResult visitExpr(SubfieldOp op);
1922 LogicalResult visitExpr(VectorCreateOp op);
1923 LogicalResult visitExpr(BundleCreateOp op);
1924 LogicalResult visitExpr(FEnumCreateOp op);
1925 LogicalResult visitExpr(AggregateConstantOp op);
1926 LogicalResult visitExpr(IsTagOp op);
1927 LogicalResult visitExpr(SubtagOp op);
1928 LogicalResult visitExpr(TagExtractOp op);
1931 if (
auto castOp = dyn_cast<mlir::UnrealizedConversionCastOp>(op))
1932 return visitUnrealizedConversionCast(castOp);
1937 LogicalResult visitDecl(WireOp op);
1938 LogicalResult visitDecl(NodeOp op);
1939 LogicalResult visitDecl(RegOp op);
1940 LogicalResult visitDecl(RegResetOp op);
1941 LogicalResult visitDecl(MemOp op);
1942 LogicalResult visitDecl(InstanceOp oldInstance);
1943 LogicalResult visitDecl(InstanceChoiceOp oldInstanceChoice);
1944 LogicalResult visitDecl(VerbatimWireOp op);
1945 LogicalResult visitDecl(ContractOp op);
1948 LogicalResult lowerNoopCast(Operation *op);
1949 LogicalResult visitExpr(AsSIntPrimOp op);
1950 LogicalResult visitExpr(AsUIntPrimOp op);
1951 LogicalResult visitExpr(AsClockPrimOp op);
1952 LogicalResult visitExpr(AsAsyncResetPrimOp op) {
return lowerNoopCast(op); }
1954 LogicalResult visitExpr(HWStructCastOp op);
1955 LogicalResult visitExpr(BitCastOp op);
1957 visitUnrealizedConversionCast(mlir::UnrealizedConversionCastOp op);
1958 LogicalResult visitExpr(CvtPrimOp op);
1959 LogicalResult visitExpr(NotPrimOp op);
1960 LogicalResult visitExpr(NegPrimOp op);
1961 LogicalResult visitExpr(PadPrimOp op);
1962 LogicalResult visitExpr(XorRPrimOp op);
1963 LogicalResult visitExpr(AndRPrimOp op);
1964 LogicalResult visitExpr(OrRPrimOp op);
1967 template <
typename ResultUnsignedOpType,
1968 typename ResultSignedOpType = ResultUnsignedOpType>
1969 LogicalResult lowerBinOp(Operation *op);
1970 template <
typename ResultOpType>
1971 LogicalResult lowerBinOpToVariadic(Operation *op);
1973 template <
typename ResultOpType>
1974 LogicalResult lowerElementwiseLogicalOp(Operation *op);
1976 LogicalResult lowerCmpOp(Operation *op, ICmpPredicate signedOp,
1977 ICmpPredicate unsignedOp);
1978 template <
typename SignedOp,
typename Un
signedOp>
1979 LogicalResult lowerDivLikeOp(Operation *op);
1981 LogicalResult visitExpr(CatPrimOp op);
1983 LogicalResult visitExpr(AndPrimOp op) {
1984 return lowerBinOpToVariadic<comb::AndOp>(op);
1986 LogicalResult visitExpr(OrPrimOp op) {
1987 return lowerBinOpToVariadic<comb::OrOp>(op);
1989 LogicalResult visitExpr(XorPrimOp op) {
1990 return lowerBinOpToVariadic<comb::XorOp>(op);
1992 LogicalResult visitExpr(ElementwiseOrPrimOp op) {
1993 return lowerElementwiseLogicalOp<comb::OrOp>(op);
1995 LogicalResult visitExpr(ElementwiseAndPrimOp op) {
1996 return lowerElementwiseLogicalOp<comb::AndOp>(op);
1998 LogicalResult visitExpr(ElementwiseXorPrimOp op) {
1999 return lowerElementwiseLogicalOp<comb::XorOp>(op);
2001 LogicalResult visitExpr(AddPrimOp op) {
2002 return lowerBinOpToVariadic<comb::AddOp>(op);
2004 LogicalResult visitExpr(EQPrimOp op) {
2005 return lowerCmpOp(op, ICmpPredicate::eq, ICmpPredicate::eq);
2007 LogicalResult visitExpr(NEQPrimOp op) {
2008 return lowerCmpOp(op, ICmpPredicate::ne, ICmpPredicate::ne);
2010 LogicalResult visitExpr(LTPrimOp op) {
2011 return lowerCmpOp(op, ICmpPredicate::slt, ICmpPredicate::ult);
2013 LogicalResult visitExpr(LEQPrimOp op) {
2014 return lowerCmpOp(op, ICmpPredicate::sle, ICmpPredicate::ule);
2016 LogicalResult visitExpr(GTPrimOp op) {
2017 return lowerCmpOp(op, ICmpPredicate::sgt, ICmpPredicate::ugt);
2019 LogicalResult visitExpr(GEQPrimOp op) {
2020 return lowerCmpOp(op, ICmpPredicate::sge, ICmpPredicate::uge);
2023 LogicalResult visitExpr(SubPrimOp op) {
return lowerBinOp<comb::SubOp>(op); }
2024 LogicalResult visitExpr(MulPrimOp op) {
2025 return lowerBinOpToVariadic<comb::MulOp>(op);
2027 LogicalResult visitExpr(DivPrimOp op) {
2028 return lowerDivLikeOp<comb::DivSOp, comb::DivUOp>(op);
2030 LogicalResult visitExpr(RemPrimOp op) {
2031 return lowerDivLikeOp<comb::ModSOp, comb::ModUOp>(op);
2035 LogicalResult visitExpr(IsXIntrinsicOp op);
2036 LogicalResult visitExpr(PlusArgsTestIntrinsicOp op);
2037 LogicalResult visitExpr(PlusArgsValueIntrinsicOp op);
2038 LogicalResult visitStmt(FPGAProbeIntrinsicOp op);
2039 LogicalResult visitExpr(ClockInverterIntrinsicOp op);
2040 LogicalResult visitExpr(ClockDividerIntrinsicOp op);
2041 LogicalResult visitExpr(SizeOfIntrinsicOp op);
2042 LogicalResult visitExpr(ClockGateIntrinsicOp op);
2043 LogicalResult visitExpr(LTLAndIntrinsicOp op);
2044 LogicalResult visitExpr(LTLOrIntrinsicOp op);
2045 LogicalResult visitExpr(LTLIntersectIntrinsicOp op);
2046 LogicalResult visitExpr(LTLDelayIntrinsicOp op);
2047 LogicalResult visitExpr(LTLConcatIntrinsicOp op);
2048 LogicalResult visitExpr(LTLRepeatIntrinsicOp op);
2049 LogicalResult visitExpr(LTLGoToRepeatIntrinsicOp op);
2050 LogicalResult visitExpr(LTLNonConsecutiveRepeatIntrinsicOp op);
2051 LogicalResult visitExpr(LTLNotIntrinsicOp op);
2052 LogicalResult visitExpr(LTLImplicationIntrinsicOp op);
2053 LogicalResult visitExpr(LTLUntilIntrinsicOp op);
2054 LogicalResult visitExpr(LTLEventuallyIntrinsicOp op);
2055 LogicalResult visitExpr(LTLClockIntrinsicOp op);
2057 template <
typename TargetOp,
typename IntrinsicOp>
2058 LogicalResult lowerVerifIntrinsicOp(IntrinsicOp op);
2059 LogicalResult visitStmt(VerifAssertIntrinsicOp op);
2060 LogicalResult visitStmt(VerifAssumeIntrinsicOp op);
2061 LogicalResult visitStmt(VerifCoverIntrinsicOp op);
2062 LogicalResult visitStmt(VerifRequireIntrinsicOp op);
2063 LogicalResult visitStmt(VerifEnsureIntrinsicOp op);
2064 LogicalResult visitExpr(HasBeenResetIntrinsicOp op);
2065 LogicalResult visitStmt(UnclockedAssumeIntrinsicOp op);
2068 LogicalResult visitExpr(BitsPrimOp op);
2069 LogicalResult visitExpr(InvalidValueOp op);
2070 LogicalResult visitExpr(HeadPrimOp op);
2071 LogicalResult visitExpr(ShlPrimOp op);
2072 LogicalResult visitExpr(ShrPrimOp op);
2073 LogicalResult visitExpr(DShlPrimOp op) {
2074 return lowerDivLikeOp<comb::ShlOp, comb::ShlOp>(op);
2076 LogicalResult visitExpr(DShrPrimOp op) {
2077 return lowerDivLikeOp<comb::ShrSOp, comb::ShrUOp>(op);
2079 LogicalResult visitExpr(DShlwPrimOp op) {
2080 return lowerDivLikeOp<comb::ShlOp, comb::ShlOp>(op);
2082 LogicalResult visitExpr(TailPrimOp op);
2083 LogicalResult visitExpr(MuxPrimOp op);
2084 LogicalResult visitExpr(Mux2CellIntrinsicOp op);
2085 LogicalResult visitExpr(Mux4CellIntrinsicOp op);
2086 LogicalResult visitExpr(MultibitMuxOp op);
2087 LogicalResult visitExpr(VerbatimExprOp op);
2088 LogicalResult visitExpr(XMRRefOp op);
2089 LogicalResult visitExpr(XMRDerefOp op);
2092 LogicalResult visitExpr(TimeOp op);
2093 LogicalResult visitExpr(HierarchicalModuleNameOp op);
2096 LogicalResult lowerVerificationStatement(
2097 Operation *op, StringRef labelPrefix, Value clock, Value predicate,
2098 Value enable, StringAttr messageAttr, ValueRange operands,
2099 StringAttr nameAttr,
bool isConcurrent, EventControl eventControl);
2101 LogicalResult visitStmt(SkipOp op);
2103 FailureOr<bool> lowerConnect(Value dest, Value srcVal);
2104 LogicalResult visitStmt(ConnectOp op);
2105 LogicalResult visitStmt(MatchingConnectOp op);
2106 LogicalResult visitStmt(ForceOp op);
2108 std::optional<Value> getLoweredFmtOperand(Value operand);
2109 LogicalResult loweredFmtOperands(ValueRange operands,
2110 SmallVectorImpl<Value> &loweredOperands);
2111 FailureOr<Value> callFileDescriptorLib(
const FileDescriptorInfo &info);
2115 LogicalResult lowerStatementWithFd(
2116 const FileDescriptorInfo &fileDescriptorInfo, Value clock, Value cond,
2117 const std::function<LogicalResult(Value)> &fn,
bool usePrintfCond);
2121 LogicalResult visitPrintfLike(T op,
2122 const FileDescriptorInfo &fileDescriptorInfo,
2123 bool usePrintfCond);
2124 LogicalResult visitStmt(PrintFOp op) {
return visitPrintfLike(op, {},
true); }
2125 LogicalResult visitStmt(FPrintFOp op);
2126 LogicalResult visitStmt(FFlushOp op);
2127 LogicalResult visitStmt(StopOp op);
2128 LogicalResult visitStmt(AssertOp op);
2129 LogicalResult visitStmt(AssumeOp op);
2130 LogicalResult visitStmt(CoverOp op);
2131 LogicalResult visitStmt(AttachOp op);
2132 LogicalResult visitStmt(RefForceOp op);
2133 LogicalResult visitStmt(RefForceInitialOp op);
2134 LogicalResult visitStmt(RefReleaseOp op);
2135 LogicalResult visitStmt(RefReleaseInitialOp op);
2136 LogicalResult visitStmt(BindOp op);
2138 FailureOr<Value> lowerSubindex(SubindexOp op, Value input);
2139 FailureOr<Value> lowerSubaccess(SubaccessOp op, Value input);
2140 FailureOr<Value> lowerSubfield(SubfieldOp op, Value input);
2142 LogicalResult fixupLTLOps();
2145 return circuitState.lowerType(type, builder.getLoc());
2153 CircuitLoweringState &circuitState;
2156 ImplicitLocOpBuilder builder;
2161 DenseMap<Value, Value> valueMapping;
2165 DenseMap<Value, Value> fromClockMapping;
2169 DenseMap<Attribute, Value> hwConstantMap;
2170 DenseMap<std::pair<Attribute, Type>, Attribute> hwAggregateConstantMap;
2174 DenseMap<unsigned, Value> hwConstantXMap;
2175 DenseMap<Type, Value> hwConstantZMap;
2181 DenseMap<Value, Value> readInOutCreated;
2184 DenseMap<StringAttr, sv::RegOp> fileNameToFileDescriptor;
2188 using AlwaysKeyType = std::tuple<
Block *, sv::EventControl, Value,
2189 sv::ResetType, sv::EventControl, Value>;
2206 llvm::MapVector<Value, Value> backedges;
2213 DenseSet<Operation *> maybeUnusedValues;
2215 void maybeUnused(Operation *op) { maybeUnusedValues.insert(op); }
2216 void maybeUnused(Value value) {
2217 if (
auto *op = value.getDefiningOp())
2229 SetVector<Operation *> ltlOpFixupWorklist;
2234 SmallVector<std::pair<Block::iterator, Block::iterator>> worklist;
2236 void addToWorklist(Block &block) {
2237 worklist.push_back({block.begin(), block.end()});
2239 void addToWorklist(Region ®ion) {
2240 for (
auto &block :
llvm::reverse(region))
2241 addToWorklist(block);
2252LogicalResult FIRRTLModuleLowering::lowerFileBody(emit::FileOp fileOp) {
2253 OpBuilder
b(&getContext());
2254 fileOp->walk([&](Operation *op) {
2255 if (
auto bindOp = dyn_cast<BindOp>(op)) {
2256 b.setInsertionPointAfter(bindOp);
2257 sv::BindOp::create(b, bindOp.getLoc(), bindOp.getInstanceAttr());
2265FIRRTLModuleLowering::lowerBody(Operation *op,
2267 if (
auto moduleOp = dyn_cast<hw::HWModuleOp>(op))
2269 if (
auto formalOp = dyn_cast<verif::FormalOp>(op))
2271 if (
auto simulationOp = dyn_cast<verif::SimulationOp>(op))
2273 if (
auto fileOp = dyn_cast<emit::FileOp>(op))
2274 return lowerFileBody(fileOp);
2279LogicalResult FIRRTLLowering::run() {
2282 for (
auto arg : theModule.
getBodyBlock()->getArguments())
2283 if (failed(setLowering(arg, arg)))
2290 addToWorklist(theModule.getBody());
2291 SmallVector<Operation *, 16> opsToRemove;
2293 while (!worklist.empty()) {
2294 auto &[opsIt, opsEnd] = worklist.back();
2295 if (opsIt == opsEnd) {
2296 worklist.pop_back();
2299 Operation *op = &*opsIt++;
2301 builder.setInsertionPoint(op);
2302 builder.setLoc(op->getLoc());
2303 auto done = succeeded(dispatchVisitor(op));
2304 circuitState.processRemainingAnnotations(op,
AnnotationSet(op));
2306 opsToRemove.push_back(op);
2308 switch (handleUnloweredOp(op)) {
2309 case AlreadyLowered:
2312 opsToRemove.push_back(op);
2314 case LoweringFailure:
2326 for (
auto &[backedge, value] : backedges) {
2327 SmallVector<Location> driverLocs;
2333 if (backedge == value) {
2334 Location edgeLoc = backedge.getLoc();
2335 if (driverLocs.empty()) {
2336 mlir::emitError(edgeLoc,
"sink does not have a driver");
2338 auto diag = mlir::emitError(edgeLoc,
"sink in combinational loop");
2339 for (
auto loc : driverLocs)
2340 diag.attachNote(loc) <<
"through driver here";
2346 auto *it = backedges.find(value);
2347 if (it == backedges.end())
2350 driverLocs.push_back(value.getLoc());
2353 if (
auto *defOp = backedge.getDefiningOp())
2354 maybeUnusedValues.erase(defOp);
2355 backedge.replaceAllUsesWith(value);
2363 while (!opsToRemove.empty()) {
2364 auto *op = opsToRemove.pop_back_val();
2369 for (
auto result : op->getResults()) {
2373 auto builder = OpBuilder::atBlockBegin(theModule.getBodyBlock());
2375 builder.getIntegerType(0), 0);
2376 maybeUnusedValues.insert(zeroI0);
2378 result.replaceAllUsesWith(zeroI0);
2381 if (!op->use_empty()) {
2382 auto d = op->emitOpError(
2383 "still has uses; should remove ops in reverse order of visitation");
2384 SmallPtrSet<Operation *, 2> visited;
2385 for (
auto *user : op->getUsers())
2386 if (visited.insert(user).second)
2388 <<
"used by " << user->
getName() <<
" op";
2391 maybeUnusedValues.erase(op);
2396 while (!maybeUnusedValues.empty()) {
2397 auto it = maybeUnusedValues.begin();
2399 maybeUnusedValues.erase(it);
2400 if (!isOpTriviallyDead(op))
2402 for (
auto operand : op->getOperands())
2403 if (auto *defOp = operand.getDefiningOp())
2404 maybeUnusedValues.insert(defOp);
2410 if (failed(fixupLTLOps()))
2421Value FIRRTLLowering::getOrCreateClockConstant(seq::ClockConst clock) {
2422 auto attr = seq::ClockConstAttr::get(theModule.getContext(), clock);
2424 auto &entry = hwConstantMap[attr];
2428 OpBuilder entryBuilder(&theModule.getBodyBlock()->front());
2429 entry = seq::ConstClockOp::create(entryBuilder, builder.getLoc(), attr);
2435Value FIRRTLLowering::getOrCreateIntConstant(
const APInt &value) {
2436 auto attr = builder.getIntegerAttr(
2437 builder.getIntegerType(value.getBitWidth()), value);
2439 auto &entry = hwConstantMap[attr];
2443 OpBuilder entryBuilder(&theModule.getBodyBlock()->front());
2450Attribute FIRRTLLowering::getOrCreateAggregateConstantAttribute(Attribute value,
2453 if (hw::type_isa<IntegerType>(type))
2454 return builder.getIntegerAttr(type, cast<IntegerAttr>(value).getValue());
2456 auto cache = hwAggregateConstantMap.lookup({value, type});
2461 SmallVector<Attribute> values;
2462 for (
auto e :
llvm::enumerate(cast<ArrayAttr>(value))) {
2464 if (
auto array = hw::type_dyn_cast<hw::ArrayType>(type))
2465 subType = array.getElementType();
2466 else if (
auto structType = hw::type_dyn_cast<hw::StructType>(type))
2467 subType = structType.getElements()[e.index()].type;
2469 assert(
false &&
"type must be either array or struct");
2471 values.push_back(getOrCreateAggregateConstantAttribute(e.value(), subType));
2475 if (hw::type_isa<hw::ArrayType>(type))
2476 std::reverse(values.begin(), values.end());
2478 auto &entry = hwAggregateConstantMap[{value, type}];
2479 entry = builder.getArrayAttr(values);
2487 const std::function<LogicalResult()> &fn) {
2488 assert(failedOperand &&
"Should be called on the failed operand");
2496Value FIRRTLLowering::getOrCreateXConstant(
unsigned numBits) {
2498 auto &entry = hwConstantXMap[numBits];
2502 OpBuilder entryBuilder(&theModule.getBodyBlock()->front());
2503 entry = sv::ConstantXOp::create(entryBuilder, builder.getLoc(),
2504 entryBuilder.getIntegerType(numBits));
2508Value FIRRTLLowering::getOrCreateZConstant(Type type) {
2509 auto &entry = hwConstantZMap[type];
2511 OpBuilder entryBuilder(&theModule.getBodyBlock()->front());
2512 entry = sv::ConstantZOp::create(entryBuilder, builder.getLoc(), type);
2521Value FIRRTLLowering::getPossiblyInoutLoweredValue(Value value) {
2523 if (
auto lowering = valueMapping.lookup(value)) {
2524 assert(!isa<FIRRTLType>(lowering.getType()) &&
2525 "Lowered value should be a non-FIRRTL value");
2534Value FIRRTLLowering::getLoweredValue(Value value) {
2535 auto result = getPossiblyInoutLoweredValue(value);
2541 if (isa<hw::InOutType>(result.getType()))
2542 return getReadValue(result);
2548Value FIRRTLLowering::getLoweredNonClockValue(Value value) {
2549 auto result = getLoweredValue(value);
2553 if (hw::type_isa<seq::ClockType>(result.getType()))
2554 return getNonClockValue(result);
2562Value FIRRTLLowering::getExtOrTruncAggregateValue(Value array,
2565 bool allowTruncate) {
2566 SmallVector<Value> resultBuffer;
2571 auto srcWidth = firrtl::type_cast<IntType>(sourceType).getWidthOrSentinel();
2572 auto destWidth = firrtl::type_cast<IntType>(destType).getWidthOrSentinel();
2573 auto resultType = builder.getIntegerType(destWidth);
2575 if (srcWidth == destWidth)
2578 if (srcWidth > destWidth) {
2582 builder.emitError(
"operand should not be a truncation");
2586 if (firrtl::type_cast<IntType>(sourceType).isSigned())
2587 return comb::createOrFoldSExt(builder, value, resultType);
2588 auto zero = getOrCreateIntConstant(destWidth - srcWidth, 0);
2596 return TypeSwitch<FIRRTLBaseType, LogicalResult>(srcType)
2597 .Case<FVectorType>([&](
auto srcVectorType) {
2598 auto destVectorType = firrtl::type_cast<FVectorType>(destType);
2599 unsigned size = resultBuffer.size();
2600 unsigned indexWidth =
2602 for (
size_t i = 0, e = std::min(srcVectorType.getNumElements(),
2603 destVectorType.getNumElements());
2605 auto iIdx = getOrCreateIntConstant(indexWidth, i);
2607 if (failed(recurse(arrayIndex, srcVectorType.getElementType(),
2608 destVectorType.getElementType())))
2611 SmallVector<Value> temp(resultBuffer.begin() + size,
2612 resultBuffer.end());
2614 resultBuffer.resize(size);
2615 resultBuffer.push_back(array);
2618 .Case<BundleType>([&](BundleType srcStructType) {
2619 auto destStructType = firrtl::type_cast<BundleType>(destType);
2620 unsigned size = resultBuffer.size();
2623 if (destStructType.getNumElements() != srcStructType.getNumElements())
2626 for (
auto elem :
llvm::enumerate(destStructType)) {
2627 auto structExtract =
2629 if (failed(recurse(structExtract,
2630 srcStructType.getElementType(elem.index()),
2631 destStructType.getElementType(elem.index()))))
2634 SmallVector<Value> temp(resultBuffer.begin() + size,
2635 resultBuffer.end());
2638 resultBuffer.resize(size);
2639 resultBuffer.push_back(newStruct);
2642 .Case<IntType>([&](
auto) {
2643 if (
auto result = cast(src, srcType, destType)) {
2644 resultBuffer.push_back(result);
2649 .Default([&](
auto) {
return failure(); });
2652 if (failed(recurse(array, sourceType, destType)))
2655 assert(resultBuffer.size() == 1 &&
2656 "resultBuffer must only contain a result array if `success` is true");
2657 return resultBuffer[0];
2665Value FIRRTLLowering::getLoweredAndExtendedValue(Value src, Type target) {
2666 auto srcType = cast<FIRRTLBaseType>(src.getType());
2667 auto dstType = cast<FIRRTLBaseType>(target);
2668 auto loweredSrc = getLoweredValue(src);
2671 auto dstWidth = dstType.getBitWidthOrSentinel();
2687 return getOrCreateIntConstant(dstWidth, 0);
2690 auto loweredSrcType = loweredSrc.getType();
2691 auto loweredDstType =
lowerType(dstType);
2694 if (loweredSrcType == loweredDstType)
2698 if (dstWidth == srcType.getBitWidthOrSentinel()) {
2700 if (loweredSrcType != loweredDstType &&
2701 (isa<hw::TypeAliasType>(loweredSrcType) ||
2702 isa<hw::TypeAliasType>(loweredDstType))) {
2703 return builder.createOrFold<
hw::BitcastOp>(loweredDstType, loweredSrc);
2708 if (isa<hw::ArrayType, hw::StructType>(loweredSrcType))
2709 return getExtOrTruncAggregateValue(loweredSrc, srcType, dstType,
2712 if (isa<seq::ClockType>(loweredSrcType)) {
2713 builder.emitError(
"cannot use clock type as an integer");
2717 auto intSourceType = dyn_cast<IntegerType>(loweredSrcType);
2718 if (!intSourceType) {
2719 builder.emitError(
"operand of type ")
2720 << loweredSrcType <<
" cannot be used as an integer";
2724 auto loweredSrcWidth = intSourceType.getWidth();
2725 if (loweredSrcWidth ==
unsigned(dstWidth))
2728 if (loweredSrcWidth >
unsigned(dstWidth)) {
2729 builder.emitError(
"operand should not be a truncation");
2734 auto valueFIRType = type_cast<FIRRTLBaseType>(src.getType()).getPassiveType();
2735 if (type_cast<IntType>(valueFIRType).isSigned())
2736 return comb::createOrFoldSExt(builder, loweredSrc, loweredDstType);
2738 auto zero = getOrCreateIntConstant(dstWidth - loweredSrcWidth, 0);
2747Value FIRRTLLowering::getLoweredAndExtOrTruncValue(Value value, Type destType) {
2748 assert(type_isa<FIRRTLBaseType>(value.getType()) &&
2749 type_isa<FIRRTLBaseType>(destType) &&
2750 "input/output value should be FIRRTL");
2753 auto destWidth = type_cast<FIRRTLBaseType>(destType).getBitWidthOrSentinel();
2754 if (destWidth == -1)
2757 auto result = getLoweredValue(value);
2769 return getOrCreateIntConstant(destWidth, 0);
2773 if (isa<hw::ArrayType, hw::StructType>(result.getType())) {
2775 if (destType == value.getType())
2778 return getExtOrTruncAggregateValue(
2779 result, type_cast<FIRRTLBaseType>(value.getType()),
2780 type_cast<FIRRTLBaseType>(destType),
2784 auto srcWidth = type_cast<IntegerType>(result.getType()).getWidth();
2785 if (srcWidth ==
unsigned(destWidth))
2791 if (srcWidth >
unsigned(destWidth)) {
2792 auto resultType = builder.getIntegerType(destWidth);
2796 auto resultType = builder.getIntegerType(destWidth);
2800 type_cast<FIRRTLBaseType>(value.getType()).getPassiveType();
2801 if (type_cast<IntType>(valueFIRType).isSigned())
2802 return comb::createOrFoldSExt(builder, result, resultType);
2804 auto zero = getOrCreateIntConstant(destWidth - srcWidth, 0);
2818std::optional<Value> FIRRTLLowering::getLoweredFmtOperand(Value operand) {
2820 if (type_isa<FStringType>(operand.getType())) {
2821 if (isa<TimeOp>(operand.getDefiningOp()))
2822 return sv::TimeOp::create(builder);
2823 if (isa<HierarchicalModuleNameOp>(operand.getDefiningOp()))
2827 auto loweredValue = getLoweredValue(operand);
2828 if (!loweredValue) {
2832 loweredValue = getOrCreateIntConstant(1, 0);
2837 if (
auto intTy = firrtl::type_cast<IntType>(operand.getType()))
2838 if (intTy.isSigned())
2839 loweredValue = sv::SystemFunctionOp::create(
2840 builder, loweredValue.getType(),
"signed", loweredValue);
2842 return loweredValue;
2846FIRRTLLowering::loweredFmtOperands(mlir::ValueRange operands,
2847 SmallVectorImpl<Value> &loweredOperands) {
2848 for (
auto operand : operands) {
2849 std::optional<Value> loweredValue = getLoweredFmtOperand(operand);
2854 loweredOperands.push_back(*loweredValue);
2859LogicalResult FIRRTLLowering::lowerStatementWithFd(
2860 const FileDescriptorInfo &fileDescriptor, Value clock, Value cond,
2861 const std::function<LogicalResult(Value)> &fn,
bool usePrintfCond) {
2863 bool failed =
false;
2864 circuitState.addMacroDecl(builder.getStringAttr(
"SYNTHESIS"));
2865 addToIfDefBlock(
"SYNTHESIS", std::function<
void()>(), [&]() {
2866 addToAlwaysBlock(clock, [&]() {
2869 circuitState.usedPrintf =
true;
2871 circuitState.addFragment(theModule,
"PRINTF_COND_FRAGMENT");
2874 Value ifCond = cond;
2875 if (usePrintfCond) {
2877 sv::MacroRefExprOp::create(builder, cond.getType(),
"PRINTF_COND_");
2878 ifCond = builder.createOrFold<
comb::AndOp>(ifCond, cond,
true);
2881 addIfProceduralBlock(ifCond, [&]() {
2885 if (fileDescriptor.isDefaultFd()) {
2887 fd = hw::ConstantOp::create(builder, APInt(32, 0x80000002));
2890 auto fdOrError = callFileDescriptorLib(fileDescriptor);
2891 if (llvm::failed(fdOrError)) {
2897 failed = llvm::failed(fn(fd));
2901 return failure(failed);
2905FIRRTLLowering::callFileDescriptorLib(
const FileDescriptorInfo &info) {
2906 circuitState.usedFileDescriptorLib =
true;
2907 circuitState.addFragment(theModule,
"CIRCT_LIB_LOGGING_FRAGMENT");
2910 if (
info.isSubstitutionRequired()) {
2911 SmallVector<Value> fileNameOperands;
2912 if (failed(loweredFmtOperands(
info.getSubstitutions(), fileNameOperands)))
2915 fileName = sv::SFormatFOp::create(builder,
info.getOutputFileFormat(),
2920 fileName = sv::ConstantStrOp::create(builder,
info.getOutputFileFormat())
2924 return sv::FuncCallProceduralOp::create(
2925 builder, mlir::TypeRange{builder.getIntegerType(32)},
2926 builder.getStringAttr(
"__circt_lib_logging::FileDescriptor::get"),
2927 ValueRange{fileName})
2937LogicalResult FIRRTLLowering::setLowering(Value orig, Value result) {
2938 if (
auto origType = dyn_cast<FIRRTLType>(orig.getType())) {
2939 assert((!result || !type_isa<FIRRTLType>(result.getType())) &&
2940 "Lowering didn't turn a FIRRTL value into a non-FIRRTL value");
2944 auto srcWidth = baseType.getPassiveType().getBitWidthOrSentinel();
2947 if (srcWidth != -1) {
2949 assert((srcWidth != 0) &&
2950 "Lowering produced value for zero width source");
2952 assert((srcWidth == 0) &&
2953 "Lowering produced null value but source wasn't zero width");
2957 assert(result &&
"Lowering of foreign type produced null value");
2960 auto &slot = valueMapping[orig];
2961 assert(!slot &&
"value lowered multiple times");
2968LogicalResult FIRRTLLowering::setPossiblyFoldedLowering(Value orig,
2972 if (
auto cst = dyn_cast_or_null<hw::ConstantOp>(result.getDefiningOp())) {
2973 auto &entry = hwConstantMap[cst.getValueAttr()];
2984 cst->moveBefore(&theModule.getBodyBlock()->front());
2988 return setLowering(orig, result);
2993template <
typename ResultOpType,
typename... CtorArgTypes>
2994LogicalResult FIRRTLLowering::setLoweringTo(Operation *orig,
2995 CtorArgTypes... args) {
2996 auto result = builder.createOrFold<ResultOpType>(args...);
2997 if (
auto *op = result.getDefiningOp())
2999 return setPossiblyFoldedLowering(orig->getResult(0), result);
3006template <
typename ResultOpType,
typename... CtorArgTypes>
3007LogicalResult FIRRTLLowering::setLoweringToLTL(Operation *orig,
3008 CtorArgTypes... args) {
3009 auto result = builder.createOrFold<ResultOpType>(args...);
3010 if (
auto *op = result.getDefiningOp())
3011 ltlOpFixupWorklist.insert(op);
3012 return setPossiblyFoldedLowering(orig->getResult(0), result);
3021Backedge FIRRTLLowering::createBackedge(Location loc, Type type) {
3022 auto backedge = backedgeBuilder.
get(type, loc);
3023 backedges.insert({backedge, backedge});
3031Backedge FIRRTLLowering::createBackedge(Value orig, Type type) {
3032 auto backedge = createBackedge(orig.getLoc(), type);
3033 (void)setLowering(orig, backedge);
3039bool FIRRTLLowering::updateIfBackedge(Value dest, Value src) {
3040 auto backedgeIt = backedges.find(dest);
3041 if (backedgeIt == backedges.end())
3043 backedgeIt->second = src;
3051void FIRRTLLowering::runWithInsertionPointAtEndOfBlock(
3052 const std::function<
void(
void)> &fn, Region ®ion) {
3056 auto oldIP = builder.saveInsertionPoint();
3058 builder.setInsertionPointToEnd(®ion.front());
3060 builder.restoreInsertionPoint(oldIP);
3064Value FIRRTLLowering::getReadValue(Value v) {
3065 Value result = readInOutCreated.lookup(v);
3071 auto oldIP = builder.saveInsertionPoint();
3072 if (
auto *vOp = v.getDefiningOp()) {
3073 builder.setInsertionPointAfter(vOp);
3077 builder.setInsertionPoint(&theModule.getBodyBlock()->front());
3082 if (
auto arrayIndexInout = v.getDefiningOp<sv::ArrayIndexInOutOp>()) {
3083 result = getReadValue(arrayIndexInout.getInput());
3085 arrayIndexInout.getIndex());
3090 builder.restoreInsertionPoint(oldIP);
3091 readInOutCreated.insert({v, result});
3095Value FIRRTLLowering::getNonClockValue(Value v) {
3096 auto it = fromClockMapping.try_emplace(v, Value{});
3098 ImplicitLocOpBuilder builder(v.getLoc(), v.getContext());
3099 builder.setInsertionPointAfterValue(v);
3100 it.first->second = seq::FromClockOp::create(builder, v);
3102 return it.first->second;
3105void FIRRTLLowering::addToAlwaysBlock(
3106 sv::EventControl clockEdge, Value clock, sv::ResetType resetStyle,
3107 sv::EventControl resetEdge, Value reset,
3108 const std::function<
void(
void)> &body,
3109 const std::function<
void(
void)> &resetBody) {
3110 AlwaysKeyType key{builder.getBlock(), clockEdge, clock,
3111 resetStyle, resetEdge, reset};
3112 sv::AlwaysOp alwaysOp;
3113 sv::IfOp insideIfOp;
3114 std::tie(alwaysOp, insideIfOp) = alwaysBlocks.lookup(key);
3118 assert(resetStyle != sv::ResetType::NoReset);
3131 auto createIfOp = [&]() {
3134 insideIfOp = sv::IfOp::create(
3135 builder, reset, [] {}, [] {});
3137 if (resetStyle == sv::ResetType::AsyncReset) {
3138 sv::EventControl events[] = {clockEdge, resetEdge};
3139 Value clocks[] = {clock, reset};
3141 alwaysOp = sv::AlwaysOp::create(builder, events, clocks, [&]() {
3142 if (resetEdge == sv::EventControl::AtNegEdge)
3143 llvm_unreachable(
"negative edge for reset is not expected");
3147 alwaysOp = sv::AlwaysOp::create(builder, clockEdge, clock, createIfOp);
3151 alwaysOp = sv::AlwaysOp::create(builder, clockEdge, clock);
3152 insideIfOp =
nullptr;
3154 alwaysBlocks[key] = {alwaysOp, insideIfOp};
3158 assert(insideIfOp &&
"reset body must be initialized before");
3159 runWithInsertionPointAtEndOfBlock(resetBody, insideIfOp.getThenRegion());
3160 runWithInsertionPointAtEndOfBlock(body, insideIfOp.getElseRegion());
3162 runWithInsertionPointAtEndOfBlock(body, alwaysOp.getBody());
3168 alwaysOp->moveBefore(builder.getInsertionBlock(),
3169 builder.getInsertionPoint());
3172LogicalResult FIRRTLLowering::emitGuards(Location loc,
3173 ArrayRef<Attribute> guards,
3174 std::function<
void(
void)>
emit) {
3175 if (guards.empty()) {
3179 auto guard = dyn_cast<StringAttr>(guards[0]);
3181 return mlir::emitError(loc,
3182 "elements in `guards` array must be `StringAttr`");
3185 circuitState.addMacroDecl(builder.getStringAttr(guard.getValue()));
3186 LogicalResult result = LogicalResult::failure();
3187 addToIfDefBlock(guard.getValue(), [&]() {
3188 result = emitGuards(loc, guards.drop_front(), emit);
3193void FIRRTLLowering::addToIfDefBlock(StringRef cond,
3194 std::function<
void(
void)> thenCtor,
3195 std::function<
void(
void)> elseCtor) {
3196 auto condAttr = builder.getStringAttr(cond);
3197 auto op = ifdefBlocks.lookup({builder.getBlock(), condAttr});
3199 runWithInsertionPointAtEndOfBlock(thenCtor, op.getThenRegion());
3200 runWithInsertionPointAtEndOfBlock(elseCtor, op.getElseRegion());
3205 op->moveBefore(builder.getInsertionBlock(), builder.getInsertionPoint());
3207 ifdefBlocks[{builder.getBlock(), condAttr}] =
3208 sv::IfDefOp::create(builder, condAttr, thenCtor, elseCtor);
3212void FIRRTLLowering::addToInitialBlock(std::function<
void(
void)> body) {
3213 auto op = initialBlocks.lookup(builder.getBlock());
3215 runWithInsertionPointAtEndOfBlock(body, op.getBody());
3220 op->moveBefore(builder.getInsertionBlock(), builder.getInsertionPoint());
3222 initialBlocks[builder.getBlock()] = sv::InitialOp::create(builder, body);
3226void FIRRTLLowering::addIfProceduralBlock(Value cond,
3227 std::function<
void(
void)> thenCtor,
3228 std::function<
void(
void)> elseCtor) {
3231 auto insertIt = builder.getInsertionPoint();
3232 if (insertIt != builder.getBlock()->begin())
3233 if (
auto ifOp = dyn_cast<sv::IfOp>(*--insertIt)) {
3234 if (ifOp.getCond() == cond) {
3235 runWithInsertionPointAtEndOfBlock(thenCtor, ifOp.getThenRegion());
3236 runWithInsertionPointAtEndOfBlock(elseCtor, ifOp.getElseRegion());
3241 sv::IfOp::create(builder, cond, thenCtor, elseCtor);
3253FIRRTLLowering::UnloweredOpResult
3254FIRRTLLowering::handleUnloweredOp(Operation *op) {
3256 if (!op->getRegions().empty() &&
3257 isa_and_nonnull<FIRRTLDialect>(op->getDialect())) {
3258 op->emitOpError(
"must explicitly handle its regions");
3259 return LoweringFailure;
3266 if (!isa_and_nonnull<FIRRTLDialect>(op->getDialect())) {
3268 for (
auto ®ion : op->getRegions())
3269 addToWorklist(region);
3270 for (
auto &operand : op->getOpOperands())
3271 if (auto lowered = getPossiblyInoutLoweredValue(operand.
get()))
3272 operand.set(lowered);
3273 for (
auto result : op->getResults())
3274 (void)setLowering(result, result);
3275 return AlreadyLowered;
3287 if (op->getNumResults() == 1) {
3288 auto resultType = op->getResult(0).getType();
3289 if (type_isa<FIRRTLBaseType>(resultType) &&
3291 (
isExpression(op) || isa<mlir::UnrealizedConversionCastOp>(op))) {
3293 (void)setLowering(op->getResult(0), Value());
3297 op->emitOpError(
"LowerToHW couldn't handle this operation");
3298 return LoweringFailure;
3301LogicalResult FIRRTLLowering::visitExpr(ConstantOp op) {
3304 return setLowering(op, Value());
3306 return setLowering(op, getOrCreateIntConstant(op.getValue()));
3309LogicalResult FIRRTLLowering::visitExpr(SpecialConstantOp op) {
3311 if (isa<ClockType>(op.getType())) {
3312 cst = getOrCreateClockConstant(op.getValue() ? seq::ClockConst::High
3313 :
seq::ClockConst::Low);
3315 cst = getOrCreateIntConstant(APInt( 1, op.getValue()));
3317 return setLowering(op, cst);
3320FailureOr<Value> FIRRTLLowering::lowerSubindex(SubindexOp op, Value input) {
3321 auto iIdx = getOrCreateIntConstant(
3323 firrtl::type_cast<FVectorType>(op.getInput().getType())
3330 if (isa<sv::InOutType>(input.getType()))
3331 result = builder.createOrFold<sv::ArrayIndexInOutOp>(input, iIdx);
3334 if (
auto *definingOp = result.getDefiningOp())
3339FailureOr<Value> FIRRTLLowering::lowerSubaccess(SubaccessOp op, Value input) {
3340 Value valueIdx = getLoweredAndExtOrTruncValue(
3342 UIntType::get(op->getContext(),
3344 firrtl::type_cast<FVectorType>(op.getInput().getType())
3345 .getNumElements())));
3347 op->emitError() <<
"input lowering failed";
3354 if (isa<sv::InOutType>(input.getType()))
3355 result = builder.createOrFold<sv::ArrayIndexInOutOp>(input, valueIdx);
3357 result = createArrayIndexing(input, valueIdx);
3358 if (
auto *definingOp = result.getDefiningOp())
3363FailureOr<Value> FIRRTLLowering::lowerSubfield(SubfieldOp op, Value input) {
3364 auto resultType =
lowerType(op->getResult(0).getType());
3365 if (!resultType || !input) {
3366 op->emitError() <<
"subfield type lowering failed";
3372 auto field = firrtl::type_cast<BundleType>(op.getInput().getType())
3373 .getElementName(op.getFieldIndex());
3375 if (isa<sv::InOutType>(input.getType()))
3376 result = builder.createOrFold<sv::StructFieldInOutOp>(input, field);
3379 if (
auto *definingOp = result.getDefiningOp())
3384LogicalResult FIRRTLLowering::visitExpr(SubindexOp op) {
3386 return setLowering(op, Value());
3388 auto input = getPossiblyInoutLoweredValue(op.getInput());
3390 return op.emitError() <<
"input lowering failed";
3392 auto result = lowerSubindex(op, input);
3395 return setLowering(op, *result);
3398LogicalResult FIRRTLLowering::visitExpr(SubaccessOp op) {
3400 return setLowering(op, Value());
3402 auto input = getPossiblyInoutLoweredValue(op.getInput());
3404 return op.emitError() <<
"input lowering failed";
3406 auto result = lowerSubaccess(op, input);
3409 return setLowering(op, *result);
3412LogicalResult FIRRTLLowering::visitExpr(SubfieldOp op) {
3415 if (getLoweredValue(op) || !op.getInput())
3419 return setLowering(op, Value());
3421 auto input = getPossiblyInoutLoweredValue(op.getInput());
3423 return op.emitError() <<
"input lowering failed";
3425 auto result = lowerSubfield(op, input);
3428 return setLowering(op, *result);
3431LogicalResult FIRRTLLowering::visitExpr(VectorCreateOp op) {
3432 auto resultType =
lowerType(op.getResult().getType());
3433 SmallVector<Value> operands;
3435 for (
auto oper :
llvm::reverse(op.getOperands())) {
3436 auto val = getLoweredValue(oper);
3439 operands.push_back(val);
3441 return setLoweringTo<hw::ArrayCreateOp>(op, resultType, operands);
3444LogicalResult FIRRTLLowering::visitExpr(BundleCreateOp op) {
3445 auto resultType =
lowerType(op.getResult().getType());
3446 SmallVector<Value> operands;
3447 for (
auto oper : op.getOperands()) {
3448 auto val = getLoweredValue(oper);
3451 operands.push_back(val);
3453 return setLoweringTo<hw::StructCreateOp>(op, resultType, operands);
3456LogicalResult FIRRTLLowering::visitExpr(FEnumCreateOp op) {
3459 return setLowering(op, Value());
3461 auto input = getLoweredValue(op.getInput());
3462 auto tagName = op.getFieldNameAttr();
3463 auto oldType = op.getType().base();
3465 auto element = *oldType.getElement(op.getFieldNameAttr());
3467 if (
auto structType = dyn_cast<hw::StructType>(newType)) {
3468 auto tagType = structType.getFieldType(
"tag");
3469 auto tagValue = IntegerAttr::get(tagType, element.value.getValue());
3470 auto tag = sv::LocalParamOp::create(builder, op.getLoc(), tagType, tagValue,
3472 auto bodyType = structType.getFieldType(
"body");
3473 auto body = hw::UnionCreateOp::create(builder, bodyType, tagName, input);
3474 SmallVector<Value> operands = {tag.getResult(), body.getResult()};
3475 return setLoweringTo<hw::StructCreateOp>(op, structType, operands);
3477 auto tagValue = IntegerAttr::get(newType, element.value.getValue());
3478 return setLoweringTo<sv::LocalParamOp>(op, newType, tagValue, tagName);
3481LogicalResult FIRRTLLowering::visitExpr(AggregateConstantOp op) {
3482 auto resultType =
lowerType(op.getResult().getType());
3484 getOrCreateAggregateConstantAttribute(op.getFieldsAttr(), resultType);
3486 return setLoweringTo<hw::AggregateConstantOp>(op, resultType,
3487 cast<ArrayAttr>(attr));
3490LogicalResult FIRRTLLowering::visitExpr(IsTagOp op) {
3492 auto tagName = op.getFieldNameAttr();
3493 auto lhs = getLoweredValue(op.getInput());
3494 if (isa<hw::StructType>(lhs.getType()))
3497 auto index = op.getFieldIndex();
3498 auto enumType = op.getInput().getType().base();
3499 auto tagValue = enumType.getElementValueAttr(index);
3500 auto tagValueType = IntegerType::get(op.getContext(), enumType.getTagWidth());
3501 auto loweredTagValue = IntegerAttr::get(tagValueType, tagValue.getValue());
3502 auto rhs = sv::LocalParamOp::create(builder, op.getLoc(), tagValueType,
3503 loweredTagValue, tagName);
3505 Type resultType = builder.getIntegerType(1);
3506 return setLoweringTo<comb::ICmpOp>(op, resultType, ICmpPredicate::eq, lhs,
3510LogicalResult FIRRTLLowering::visitExpr(SubtagOp op) {
3513 return setLowering(op, Value());
3515 auto tagName = op.getFieldNameAttr();
3516 auto input = getLoweredValue(op.getInput());
3518 return setLoweringTo<hw::UnionExtractOp>(op, field, tagName);
3521LogicalResult FIRRTLLowering::visitExpr(TagExtractOp op) {
3524 return setLowering(op, Value());
3526 auto input = getLoweredValue(op.getInput());
3532 if (isa<hw::StructType>(input.getType())) {
3533 return setLoweringTo<hw::StructExtractOp>(op, input,
"tag");
3538 return setLowering(op, input);
3545LogicalResult FIRRTLLowering::visitDecl(WireOp op) {
3546 auto origResultType = op.getResult().getType();
3550 if (!type_isa<FIRRTLType>(origResultType)) {
3551 createBackedge(op.getResult(), origResultType);
3555 auto resultType =
lowerType(origResultType);
3559 if (resultType.isInteger(0)) {
3560 if (op.getInnerSym())
3561 return op.emitError(
"zero width wire is referenced by name [")
3562 << *op.getInnerSym() <<
"] (e.g. in an XMR) but must be removed";
3563 return setLowering(op.getResult(), Value());
3567 auto innerSym = lowerInnerSymbol(op);
3568 auto name = op.getNameAttr();
3571 auto wire = hw::WireOp::create(
3572 builder, op.getLoc(), getOrCreateZConstant(resultType), name, innerSym);
3574 if (
auto svAttrs = sv::getSVAttributes(op))
3575 sv::setSVAttributes(wire, svAttrs);
3577 return setLowering(op.getResult(), wire);
3580LogicalResult FIRRTLLowering::visitDecl(VerbatimWireOp op) {
3581 auto resultTy =
lowerType(op.getType());
3584 resultTy = sv::InOutType::get(op.getContext(), resultTy);
3586 SmallVector<Value, 4> operands;
3587 operands.reserve(op.getSubstitutions().size());
3588 for (
auto operand : op.getSubstitutions()) {
3589 auto lowered = getLoweredValue(operand);
3592 operands.push_back(lowered);
3595 ArrayAttr symbols = op.getSymbolsAttr();
3597 symbols = ArrayAttr::get(op.getContext(), {});
3599 return setLoweringTo<sv::VerbatimExprSEOp>(op, resultTy, op.getTextAttr(),
3603LogicalResult FIRRTLLowering::visitDecl(NodeOp op) {
3604 auto operand = getLoweredValue(op.getInput());
3606 return handleZeroBit(op.getInput(), [&]() -> LogicalResult {
3607 if (op.getInnerSym())
3608 return op.emitError(
"zero width node is referenced by name [")
3609 << *op.getInnerSym()
3610 <<
"] (e.g. in an XMR) but must be "
3612 return setLowering(op.getResult(), Value());
3618 auto name = op.getNameAttr();
3619 auto innerSym = lowerInnerSymbol(op);
3622 operand = hw::WireOp::create(builder, operand, name, innerSym);
3625 if (
auto svAttrs = sv::getSVAttributes(op)) {
3627 operand = hw::WireOp::create(builder, operand, name);
3628 sv::setSVAttributes(operand.getDefiningOp(), svAttrs);
3631 return setLowering(op.getResult(), operand);
3634LogicalResult FIRRTLLowering::visitDecl(RegOp op) {
3635 auto resultType =
lowerType(op.getResult().getType());
3638 if (resultType.isInteger(0))
3639 return setLowering(op.getResult(), Value());
3641 Value clockVal = getLoweredValue(op.getClockVal());
3646 auto innerSym = lowerInnerSymbol(op);
3647 Backedge inputEdge = backedgeBuilder.
get(resultType);
3648 auto reg = seq::FirRegOp::create(builder, inputEdge, clockVal,
3649 op.getNameAttr(), innerSym);
3652 if (
auto randomRegister = op->getAttr(
"firrtl.random_init_register"))
3653 reg->setAttr(
"firrtl.random_init_register", randomRegister);
3654 if (
auto randomStart = op->getAttr(
"firrtl.random_init_start"))
3655 reg->setAttr(
"firrtl.random_init_start", randomStart);
3656 if (
auto randomEnd = op->getAttr(
"firrtl.random_init_end"))
3657 reg->setAttr(
"firrtl.random_init_end", randomEnd);
3660 if (
auto svAttrs = sv::getSVAttributes(op))
3661 sv::setSVAttributes(reg, svAttrs);
3664 (void)setLowering(op.getResult(),
reg);
3668LogicalResult FIRRTLLowering::visitDecl(RegResetOp op) {
3669 auto resultType =
lowerType(op.getResult().getType());
3672 if (resultType.isInteger(0))
3673 return setLowering(op.getResult(), Value());
3675 Value clockVal = getLoweredValue(op.getClockVal());
3676 Value resetSignal = getLoweredValue(op.getResetSignal());
3678 Value resetValue = getLoweredAndExtOrTruncValue(
3679 op.getResetValue(), type_cast<FIRRTLBaseType>(op.getResult().getType()));
3681 if (!clockVal || !resetSignal || !resetValue)
3685 auto innerSym = lowerInnerSymbol(op);
3686 bool isAsync = type_isa<AsyncResetType>(op.getResetSignal().getType());
3687 Backedge inputEdge = backedgeBuilder.
get(resultType);
3689 seq::FirRegOp::create(builder, inputEdge, clockVal, op.getNameAttr(),
3690 resetSignal, resetValue, innerSym, isAsync);
3693 if (
auto randomRegister = op->getAttr(
"firrtl.random_init_register"))
3694 reg->setAttr(
"firrtl.random_init_register", randomRegister);
3695 if (
auto randomStart = op->getAttr(
"firrtl.random_init_start"))
3696 reg->setAttr(
"firrtl.random_init_start", randomStart);
3697 if (
auto randomEnd = op->getAttr(
"firrtl.random_init_end"))
3698 reg->setAttr(
"firrtl.random_init_end", randomEnd);
3701 if (
auto svAttrs = sv::getSVAttributes(op))
3702 sv::setSVAttributes(reg, svAttrs);
3705 (void)setLowering(op.getResult(),
reg);
3710LogicalResult FIRRTLLowering::visitDecl(MemOp op) {
3713 if (type_isa<BundleType>(op.getDataType()))
3714 return op.emitOpError(
3715 "should have already been lowered from a ground type to an aggregate "
3716 "type using the LowerTypes pass. Use "
3717 "'firtool --lower-types' or 'circt-opt "
3718 "--pass-pipeline='firrtl.circuit(firrtl-lower-types)' "
3724 auto memType = seq::FirMemType::get(
3727 : std::optional<uint32_t>());
3729 seq::FirMemInitAttr memInit;
3730 if (
auto init = op.getInitAttr())
3731 memInit = seq::FirMemInitAttr::get(init.getContext(), init.getFilename(),
3732 init.getIsBinary(), init.getIsInline());
3734 auto memDecl = seq::FirMemOp::create(
3737 op.getInnerSymAttr(), memInit, op.getPrefixAttr(), Attribute{});
3740 if (
auto file = parent->getAttrOfType<hw::OutputFileAttr>(
"output_file")) {
3742 if (!file.isDirectory())
3743 dir = hw::OutputFileAttr::getAsDirectory(builder.getContext(),
3744 file.getDirectory());
3745 memDecl.setOutputFileAttr(dir);
3751 for (
size_t i = 0, e = op.getNumResults(); i != e; ++i) {
3753 auto addOutput = [&](StringRef field,
size_t width, Value value) {
3756 (void)setLowering(a, value);
3762 auto addInput = [&](StringRef field, Value backedge) {
3764 if (cast<FIRRTLBaseType>(
a.getType())
3766 .getBitWidthOrSentinel() > 0)
3767 (void)setLowering(a, backedge);
3773 auto addInputPort = [&](StringRef field,
size_t width) -> Value {
3777 Value backedge, portValue;
3779 portValue = getOrCreateXConstant(1);
3781 auto portType = IntegerType::get(op.getContext(), width);
3782 backedge = portValue = createBackedge(builder.getLoc(), portType);
3784 addInput(field, backedge);
3788 auto addClock = [&](StringRef field) -> Value {
3789 Type clockTy = seq::ClockType::get(op.getContext());
3790 Value portValue = createBackedge(builder.getLoc(), clockTy);
3791 addInput(field, portValue);
3795 auto memportKind = op.getPortKind(i);
3796 if (memportKind == MemOp::PortKind::Read) {
3797 auto addr = addInputPort(
"addr", op.getAddrBits());
3798 auto en = addInputPort(
"en", 1);
3799 auto clk = addClock(
"clk");
3800 auto data = seq::FirMemReadOp::create(builder, memDecl,
addr,
clk,
en);
3802 }
else if (memportKind == MemOp::PortKind::ReadWrite) {
3803 auto addr = addInputPort(
"addr", op.getAddrBits());
3804 auto en = addInputPort(
"en", 1);
3805 auto clk = addClock(
"clk");
3808 auto mode = addInputPort(
"wmode", 1);
3810 mode = builder.createOrFold<
comb::AndOp>(mode, addInputPort(
"wmask", 1),
3817 auto rdata = seq::FirMemReadWriteOp::create(builder, memDecl,
addr,
clk,
3821 auto addr = addInputPort(
"addr", op.getAddrBits());
3824 auto en = addInputPort(
"en", 1);
3828 auto clk = addClock(
"clk");
3842FIRRTLLowering::prepareInstanceOperands(ArrayRef<PortInfo> portInfo,
3843 Operation *instanceOp,
3844 SmallVectorImpl<Value> &inputOperands) {
3846 for (
size_t portIndex = 0, e = portInfo.size(); portIndex != e; ++portIndex) {
3847 auto &port = portInfo[portIndex];
3850 instanceOp->emitOpError(
"could not lower type of port ") << port.name;
3855 if (portType.isInteger(0))
3859 if (port.isOutput())
3862 auto portResult = instanceOp->getResult(portIndex);
3863 assert(portResult &&
"invalid IR, couldn't find port");
3867 if (port.isInput()) {
3868 inputOperands.push_back(createBackedge(portResult, portType));
3874 if (type_isa<AnalogType>(portResult.getType()) && portResult.hasOneUse()) {
3875 if (
auto attach = dyn_cast<AttachOp>(*portResult.getUsers().begin())) {
3877 auto loweredResult = getPossiblyInoutLoweredValue(source);
3878 inputOperands.push_back(loweredResult);
3879 (void)setLowering(portResult, loweredResult);
3888 "." + port.getName().str() +
".wire");
3892 (void)setLowering(portResult, wire);
3893 inputOperands.push_back(wire);
3899LogicalResult FIRRTLLowering::visitDecl(InstanceOp oldInstance) {
3900 Operation *oldModule =
3901 oldInstance.getReferencedModule(circuitState.getInstanceGraph());
3903 auto *newModule = circuitState.getNewModule(oldModule);
3905 oldInstance->emitOpError(
"could not find module [")
3906 << oldInstance.getModuleName() <<
"] referenced by instance";
3912 ArrayAttr parameters;
3913 if (
auto oldExtModule = dyn_cast<FExtModuleOp>(oldModule))
3918 SmallVector<PortInfo, 8> portInfo = cast<FModuleLike>(oldModule).getPorts();
3922 SmallVector<Value, 8> operands;
3923 if (failed(prepareInstanceOperands(portInfo, oldInstance, operands)))
3930 auto innerSym = oldInstance.getInnerSymAttr();
3931 if (oldInstance.getLowerToBind()) {
3934 oldInstance.getContext(), oldInstance.getInnerSymAttr(), 0,
3937 auto bindOp = sv::BindOp::create(builder, theModule.getNameAttr(),
3938 innerSym.getSymName());
3941 if (
auto outputFile = oldInstance->getAttr(
"output_file"))
3942 bindOp->setAttr(
"output_file", outputFile);
3945 circuitState.addBind(bindOp);
3950 hw::InstanceOp::create(builder, newModule, oldInstance.getNameAttr(),
3951 operands, parameters, innerSym);
3953 if (oldInstance.getLowerToBind() || oldInstance.getDoNotPrint())
3954 newInstance.setDoNotPrintAttr(builder.getUnitAttr());
3956 if (newInstance.getInnerSymAttr())
3957 if (
auto forceName = circuitState.instanceForceNames.lookup(
3958 {newInstance->getParentOfType<hw::HWModuleOp>().getNameAttr(),
3959 newInstance.getInnerNameAttr()}))
3960 newInstance->setAttr(
"hw.verilogName", forceName);
3964 unsigned resultNo = 0;
3965 for (
size_t portIndex = 0, e = portInfo.size(); portIndex != e; ++portIndex) {
3966 auto &port = portInfo[portIndex];
3970 Value resultVal = newInstance.getResult(resultNo);
3972 auto oldPortResult = oldInstance.getResult(portIndex);
3973 (void)setLowering(oldPortResult, resultVal);
3979LogicalResult FIRRTLLowering::visitDecl(InstanceChoiceOp oldInstanceChoice) {
3980 if (oldInstanceChoice.getInnerSymAttr()) {
3981 oldInstanceChoice->emitOpError(
3982 "instance choice with inner sym cannot be lowered");
3987 FlatSymbolRefAttr instanceMacro = oldInstanceChoice.getInstanceMacroAttr();
3989 return oldInstanceChoice->emitOpError(
3990 "must have instance_macro attribute set before "
3994 auto moduleNames = oldInstanceChoice.getModuleNamesAttr();
3995 auto caseNames = oldInstanceChoice.getCaseNamesAttr();
3998 auto defaultModuleName = oldInstanceChoice.getDefaultTargetAttr();
3999 auto *defaultModuleNode =
4000 circuitState.getInstanceGraph().lookup(defaultModuleName.getAttr());
4002 Operation *defaultModule = defaultModuleNode->getModule();
4006 SmallVector<PortInfo, 8> portInfo =
4007 cast<FModuleLike>(defaultModule).getPorts();
4010 SmallVector<Value, 8> inputOperands;
4012 prepareInstanceOperands(portInfo, oldInstanceChoice, inputOperands)))
4016 SmallVector<sv::WireOp, 8> outputWires;
4017 StringRef wirePrefix = oldInstanceChoice.getInstanceName();
4018 for (
size_t portIndex = 0, e = portInfo.size(); portIndex != e; ++portIndex) {
4019 auto &port = portInfo[portIndex];
4023 if (!portType || portType.isInteger(0))
4026 builder, portType, wirePrefix.str() +
"." + port.getName().str());
4027 outputWires.push_back(wire);
4028 if (failed(setLowering(oldInstanceChoice.getResult(portIndex), wire)))
4033 auto createInstanceAndAssign = [&](Operation *oldMod,
4034 StringRef suffix) -> LogicalResult {
4035 auto *newMod = circuitState.getNewModule(oldMod);
4037 return oldInstanceChoice->emitOpError(
"could not find lowered module");
4039 ArrayAttr parameters;
4040 if (
auto oldExtModule = dyn_cast<FExtModuleOp>(oldMod))
4044 SmallString<64> instName;
4045 instName = oldInstanceChoice.getInstanceName();
4046 if (!suffix.empty()) {
4050 auto instNameAttr = builder.getStringAttr(instName);
4053 auto inst = hw::InstanceOp::create(builder, newMod, instNameAttr,
4054 inputOperands, parameters,
4058 for (
unsigned i = 0; i < inst.getNumResults(); ++i)
4064 if (failed(createInstanceAndAssign(defaultModule,
"default")))
4068 for (
size_t i = 0; i < caseNames.size(); ++i) {
4069 auto caseSymRef = cast<SymbolRefAttr>(caseNames[i]);
4070 auto caseName = caseSymRef.getLeafReference();
4071 auto targetModuleRef = cast<FlatSymbolRefAttr>(moduleNames[i + 1]);
4073 Operation *altModule = circuitState.getInstanceGraph()
4074 .lookup(targetModuleRef.getAttr())
4077 return oldInstanceChoice->emitOpError(
4078 "could not find alternative module [")
4079 << targetModuleRef <<
"] referenced by instance choice";
4082 if (failed(createInstanceAndAssign(altModule, caseName.getValue())))
4089LogicalResult FIRRTLLowering::visitDecl(ContractOp oldOp) {
4090 SmallVector<Value> inputs;
4091 SmallVector<Type> types;
4092 for (
auto input : oldOp.getInputs()) {
4093 auto lowered = getLoweredValue(input);
4096 inputs.push_back(lowered);
4097 types.push_back(lowered.getType());
4100 auto newOp = verif::ContractOp::create(builder, types, inputs);
4101 newOp->setDiscardableAttrs(oldOp->getDiscardableAttrDictionary());
4102 auto &body = newOp.getBody().emplaceBlock();
4104 for (
auto [newResult, oldResult, oldArg] :
4105 llvm::zip(newOp.getResults(), oldOp.getResults(),
4106 oldOp.getBody().getArguments())) {
4107 if (failed(setLowering(oldResult, newResult)))
4109 if (failed(setLowering(oldArg, newResult)))
4113 body.getOperations().splice(body.end(),
4114 oldOp.getBody().front().getOperations());
4115 addToWorklist(body);
4125LogicalResult FIRRTLLowering::lowerNoopCast(Operation *op) {
4126 auto operand = getPossiblyInoutLoweredValue(op->getOperand(0));
4131 return setLowering(op->getResult(0), operand);
4134LogicalResult FIRRTLLowering::visitExpr(AsSIntPrimOp op) {
4135 if (isa<ClockType>(op.getInput().getType()))
4136 return setLowering(op->getResult(0),
4137 getLoweredNonClockValue(op.getInput()));
4138 return lowerNoopCast(op);
4141LogicalResult FIRRTLLowering::visitExpr(AsUIntPrimOp op) {
4142 if (isa<ClockType>(op.getInput().getType()))
4143 return setLowering(op->getResult(0),
4144 getLoweredNonClockValue(op.getInput()));
4145 return lowerNoopCast(op);
4148LogicalResult FIRRTLLowering::visitExpr(AsClockPrimOp op) {
4149 return setLoweringTo<seq::ToClockOp>(op, getLoweredValue(op.getInput()));
4152LogicalResult FIRRTLLowering::visitUnrealizedConversionCast(
4153 mlir::UnrealizedConversionCastOp op) {
4155 if (op.getNumOperands() != 1 || op.getNumResults() != 1)
4158 auto operand = op.getOperand(0);
4159 auto result = op.getResult(0);
4162 if (type_isa<FIRRTLType>(operand.getType()) &&
4163 type_isa<FIRRTLType>(result.getType()))
4164 return lowerNoopCast(op);
4168 if (!type_isa<FIRRTLType>(operand.getType())) {
4169 if (type_isa<FIRRTLType>(result.getType()))
4170 return setLowering(result, getPossiblyInoutLoweredValue(operand));
4176 auto loweredResult = getLoweredValue(operand);
4177 if (!loweredResult) {
4180 if (operand.getType().isSignlessInteger(0)) {
4181 return setLowering(result, Value());
4188 result.replaceAllUsesWith(loweredResult);
4192LogicalResult FIRRTLLowering::visitExpr(HWStructCastOp op) {
4195 if (
auto opStructType = dyn_cast<hw::StructType>(op.getOperand().getType()))
4196 return setLowering(op, op.getOperand());
4200 auto result = getLoweredValue(op.getOperand());
4206 op.replaceAllUsesWith(result);
4210LogicalResult FIRRTLLowering::visitExpr(BitCastOp op) {
4211 auto operand = getLoweredValue(op.getOperand());
4214 auto resultType =
lowerType(op.getType());
4218 return setLoweringTo<hw::BitcastOp>(op, resultType, operand);
4221LogicalResult FIRRTLLowering::visitExpr(CvtPrimOp op) {
4222 auto operand = getLoweredValue(op.getOperand());
4226 if (type_cast<IntType>(op.getOperand().getType()).isUnsigned())
4227 return setLowering(op, getOrCreateIntConstant(1, 0));
4229 return setLowering(op, Value());
4234 if (type_cast<IntType>(op.getOperand().getType()).isSigned())
4235 return setLowering(op, operand);
4238 auto zero = getOrCreateIntConstant(1, 0);
4239 return setLoweringTo<comb::ConcatOp>(op, zero, operand);
4242LogicalResult FIRRTLLowering::visitExpr(NotPrimOp op) {
4243 auto operand = getLoweredValue(op.getInput());
4247 auto allOnes = getOrCreateIntConstant(
4248 APInt::getAllOnes(operand.getType().getIntOrFloatBitWidth()));
4249 return setLoweringTo<comb::XorOp>(op, operand, allOnes,
true);
4252LogicalResult FIRRTLLowering::visitExpr(NegPrimOp op) {
4255 auto operand = getLoweredAndExtendedValue(op.getInput(), op.getType());
4259 auto resultType =
lowerType(op.getType());
4261 auto zero = getOrCreateIntConstant(resultType.getIntOrFloatBitWidth(), 0);
4262 return setLoweringTo<comb::SubOp>(op, zero, operand,
true);
4266LogicalResult FIRRTLLowering::visitExpr(PadPrimOp op) {
4267 auto operand = getLoweredAndExtendedValue(op.getInput(), op.getType());
4270 return setLowering(op, operand);
4273LogicalResult FIRRTLLowering::visitExpr(XorRPrimOp op) {
4274 auto operand = getLoweredValue(op.getInput());
4277 return setLowering(op, getOrCreateIntConstant(1, 0));
4282 return setLoweringTo<comb::ParityOp>(op, builder.getIntegerType(1), operand,
4286LogicalResult FIRRTLLowering::visitExpr(AndRPrimOp op) {
4287 auto operand = getLoweredValue(op.getInput());
4290 return setLowering(op, getOrCreateIntConstant(1, 1));
4295 return setLoweringTo<comb::ICmpOp>(
4296 op, ICmpPredicate::eq, operand,
4297 getOrCreateIntConstant(
4298 APInt::getAllOnes(operand.getType().getIntOrFloatBitWidth())),
4302LogicalResult FIRRTLLowering::visitExpr(OrRPrimOp op) {
4303 auto operand = getLoweredValue(op.getInput());
4306 return setLowering(op, getOrCreateIntConstant(1, 0));
4312 return setLoweringTo<comb::ICmpOp>(
4313 op, ICmpPredicate::ne, operand,
4314 getOrCreateIntConstant(operand.getType().getIntOrFloatBitWidth(), 0),
4322template <
typename ResultOpType>
4323LogicalResult FIRRTLLowering::lowerBinOpToVariadic(Operation *op) {
4324 auto resultType = op->getResult(0).getType();
4325 auto lhs = getLoweredAndExtendedValue(op->getOperand(0), resultType);
4326 auto rhs = getLoweredAndExtendedValue(op->getOperand(1), resultType);
4330 return setLoweringTo<ResultOpType>(op, lhs, rhs,
true);
4336template <
typename ResultOpType>
4337LogicalResult FIRRTLLowering::lowerElementwiseLogicalOp(Operation *op) {
4338 auto resultType = op->getResult(0).getType();
4339 auto lhs = getLoweredAndExtendedValue(op->getOperand(0), resultType);
4340 auto rhs = getLoweredAndExtendedValue(op->getOperand(1), resultType);
4351 auto intType = builder.getIntegerType(*bitwidth);
4352 auto retType = lhs.getType();
4355 auto result = builder.createOrFold<ResultOpType>(lhs, rhs,
true);
4356 return setLoweringTo<hw::BitcastOp>(op, retType, result);
4361template <
typename ResultUn
signedOpType,
typename ResultSignedOpType>
4362LogicalResult FIRRTLLowering::lowerBinOp(Operation *op) {
4364 auto resultType = op->getResult(0).getType();
4365 auto lhs = getLoweredAndExtendedValue(op->getOperand(0), resultType);
4366 auto rhs = getLoweredAndExtendedValue(op->getOperand(1), resultType);
4371 if (type_cast<IntType>(resultType).isSigned())
4372 return setLoweringTo<ResultSignedOpType>(op, lhs, rhs,
true);
4373 return setLoweringTo<ResultUnsignedOpType>(op, lhs, rhs,
true);
4378LogicalResult FIRRTLLowering::lowerCmpOp(Operation *op, ICmpPredicate signedOp,
4379 ICmpPredicate unsignedOp) {
4381 auto lhsIntType = type_cast<IntType>(op->getOperand(0).getType());
4382 auto rhsIntType = type_cast<IntType>(op->getOperand(1).getType());
4383 if (!lhsIntType.hasWidth() || !rhsIntType.hasWidth())
4387 if (cmpType.getWidth() == 0)
4388 cmpType = UIntType::get(builder.getContext(), 1);
4389 auto lhs = getLoweredAndExtendedValue(op->getOperand(0), cmpType);
4390 auto rhs = getLoweredAndExtendedValue(op->getOperand(1), cmpType);
4395 Type resultType = builder.getIntegerType(1);
4396 return setLoweringTo<comb::ICmpOp>(
4397 op, resultType, lhsIntType.isSigned() ? signedOp : unsignedOp, lhs, rhs,
4403template <
typename SignedOp,
typename Un
signedOp>
4404LogicalResult FIRRTLLowering::lowerDivLikeOp(Operation *op) {
4408 auto opType = type_cast<IntType>(op->getResult(0).getType());
4409 if (opType.getWidth() == 0)
4410 return setLowering(op->getResult(0), Value());
4414 auto lhs = getLoweredAndExtendedValue(op->getOperand(0), resultType);
4415 auto rhs = getLoweredAndExtendedValue(op->getOperand(1), resultType);
4420 if (opType.isSigned())
4421 result = builder.createOrFold<SignedOp>(lhs, rhs,
true);
4423 result = builder.createOrFold<UnsignedOp>(lhs, rhs,
true);
4425 if (
auto *definingOp = result.getDefiningOp())
4428 if (resultType == opType)
4429 return setLowering(op->getResult(0), result);
4430 return setLoweringTo<comb::ExtractOp>(op,
lowerType(opType), result, 0);
4433LogicalResult FIRRTLLowering::visitExpr(CatPrimOp op) {
4435 if (op.getInputs().empty())
4436 return setLowering(op, Value());
4438 SmallVector<Value> loweredOperands;
4441 for (
auto operand : op.getInputs()) {
4442 auto loweredOperand = getLoweredValue(operand);
4443 if (loweredOperand) {
4444 loweredOperands.push_back(loweredOperand);
4447 auto result =
handleZeroBit(operand, [&]() {
return success(); });
4455 if (loweredOperands.empty())
4456 return setLowering(op, Value());
4459 return setLoweringTo<comb::ConcatOp>(op, loweredOperands);
4466LogicalResult FIRRTLLowering::visitExpr(IsXIntrinsicOp op) {
4467 auto input = getLoweredNonClockValue(op.getArg());
4471 if (!isa<IntType>(input.getType())) {
4472 auto srcType = op.getArg().getType();
4474 assert(bitwidth &&
"Unknown width");
4475 auto intType = builder.getIntegerType(*bitwidth);
4476 input = builder.createOrFold<
hw::BitcastOp>(intType, input);
4479 return setLoweringTo<comb::ICmpOp>(
4480 op, ICmpPredicate::ceq, input,
4481 getOrCreateXConstant(input.getType().getIntOrFloatBitWidth()),
true);
4484LogicalResult FIRRTLLowering::visitStmt(FPGAProbeIntrinsicOp op) {
4485 auto operand = getLoweredValue(op.getInput());
4486 hw::WireOp::create(builder, operand);
4490LogicalResult FIRRTLLowering::visitExpr(PlusArgsTestIntrinsicOp op) {
4491 return setLoweringTo<sim::PlusArgsTestOp>(op, builder.getIntegerType(1),
4492 op.getFormatStringAttr());
4495LogicalResult FIRRTLLowering::visitExpr(PlusArgsValueIntrinsicOp op) {
4496 auto type =
lowerType(op.getResult().getType());
4500 auto valueOp = sim::PlusArgsValueOp::create(
4501 builder, builder.getIntegerType(1), type, op.getFormatStringAttr());
4502 if (failed(setLowering(op.getResult(), valueOp.getResult())))
4504 if (failed(setLowering(op.getFound(), valueOp.getFound())))
4509LogicalResult FIRRTLLowering::visitExpr(SizeOfIntrinsicOp op) {
4510 op.emitError(
"SizeOf should have been resolved.");
4514LogicalResult FIRRTLLowering::visitExpr(ClockGateIntrinsicOp op) {
4516 if (op.getTestEnable())
4517 testEnable = getLoweredValue(op.getTestEnable());
4518 return setLoweringTo<seq::ClockGateOp>(
4519 op, getLoweredValue(op.getInput()), getLoweredValue(op.getEnable()),
4520 testEnable, hw::InnerSymAttr{});
4523LogicalResult FIRRTLLowering::visitExpr(ClockInverterIntrinsicOp op) {
4524 auto operand = getLoweredValue(op.getInput());
4525 return setLoweringTo<seq::ClockInverterOp>(op, operand);
4528LogicalResult FIRRTLLowering::visitExpr(ClockDividerIntrinsicOp op) {
4529 auto operand = getLoweredValue(op.getInput());
4530 return setLoweringTo<seq::ClockDividerOp>(op, operand, op.getPow2());
4533LogicalResult FIRRTLLowering::visitExpr(LTLAndIntrinsicOp op) {
4534 return setLoweringToLTL<ltl::AndOp>(
4536 ValueRange{getLoweredValue(op.getLhs()), getLoweredValue(op.getRhs())});
4539LogicalResult FIRRTLLowering::visitExpr(LTLOrIntrinsicOp op) {
4540 return setLoweringToLTL<ltl::OrOp>(
4542 ValueRange{getLoweredValue(op.getLhs()), getLoweredValue(op.getRhs())});
4545LogicalResult FIRRTLLowering::visitExpr(LTLIntersectIntrinsicOp op) {
4546 return setLoweringToLTL<ltl::IntersectOp>(
4548 ValueRange{getLoweredValue(op.getLhs()), getLoweredValue(op.getRhs())});
4551LogicalResult FIRRTLLowering::visitExpr(LTLDelayIntrinsicOp op) {
4552 return setLoweringToLTL<ltl::DelayOp>(op, getLoweredValue(op.getInput()),
4553 op.getDelayAttr(), op.getLengthAttr());
4556LogicalResult FIRRTLLowering::visitExpr(LTLConcatIntrinsicOp op) {
4557 return setLoweringToLTL<ltl::ConcatOp>(
4559 ValueRange{getLoweredValue(op.getLhs()), getLoweredValue(op.getRhs())});
4562LogicalResult FIRRTLLowering::visitExpr(LTLRepeatIntrinsicOp op) {
4563 return setLoweringToLTL<ltl::RepeatOp>(op, getLoweredValue(op.getInput()),
4564 op.getBaseAttr(), op.getMoreAttr());
4567LogicalResult FIRRTLLowering::visitExpr(LTLGoToRepeatIntrinsicOp op) {
4568 return setLoweringToLTL<ltl::GoToRepeatOp>(
4569 op, getLoweredValue(op.getInput()), op.getBaseAttr(), op.getMoreAttr());
4572LogicalResult FIRRTLLowering::visitExpr(LTLNonConsecutiveRepeatIntrinsicOp op) {
4573 return setLoweringToLTL<ltl::NonConsecutiveRepeatOp>(
4574 op, getLoweredValue(op.getInput()), op.getBaseAttr(), op.getMoreAttr());
4577LogicalResult FIRRTLLowering::visitExpr(LTLNotIntrinsicOp op) {
4578 return setLoweringToLTL<ltl::NotOp>(op, getLoweredValue(op.getInput()));
4581LogicalResult FIRRTLLowering::visitExpr(LTLImplicationIntrinsicOp op) {
4582 return setLoweringToLTL<ltl::ImplicationOp>(
4584 ValueRange{getLoweredValue(op.getLhs()), getLoweredValue(op.getRhs())});
4587LogicalResult FIRRTLLowering::visitExpr(LTLUntilIntrinsicOp op) {
4588 return setLoweringToLTL<ltl::UntilOp>(
4590 ValueRange{getLoweredValue(op.getLhs()), getLoweredValue(op.getRhs())});
4593LogicalResult FIRRTLLowering::visitExpr(LTLEventuallyIntrinsicOp op) {
4594 return setLoweringToLTL<ltl::EventuallyOp>(op,
4595 getLoweredValue(op.getInput()));
4598LogicalResult FIRRTLLowering::visitExpr(LTLClockIntrinsicOp op) {
4599 return setLoweringToLTL<ltl::ClockOp>(op, getLoweredValue(op.getInput()),
4600 ltl::ClockEdge::Pos,
4601 getLoweredNonClockValue(op.getClock()));
4604template <
typename TargetOp,
typename IntrinsicOp>
4605LogicalResult FIRRTLLowering::lowerVerifIntrinsicOp(IntrinsicOp op) {
4606 auto property = getLoweredValue(op.getProperty());
4607 auto enable = op.getEnable() ? getLoweredValue(op.getEnable()) : Value();
4608 TargetOp::create(builder, property, enable, op.getLabelAttr());
4612LogicalResult FIRRTLLowering::visitStmt(VerifAssertIntrinsicOp op) {
4613 return lowerVerifIntrinsicOp<verif::AssertOp>(op);
4616LogicalResult FIRRTLLowering::visitStmt(VerifAssumeIntrinsicOp op) {
4617 return lowerVerifIntrinsicOp<verif::AssumeOp>(op);
4620LogicalResult FIRRTLLowering::visitStmt(VerifCoverIntrinsicOp op) {
4621 return lowerVerifIntrinsicOp<verif::CoverOp>(op);
4624LogicalResult FIRRTLLowering::visitStmt(VerifRequireIntrinsicOp op) {
4625 if (!isa<verif::ContractOp>(op->getParentOp()))
4626 return lowerVerifIntrinsicOp<verif::AssertOp>(op);
4627 return lowerVerifIntrinsicOp<verif::RequireOp>(op);
4630LogicalResult FIRRTLLowering::visitStmt(VerifEnsureIntrinsicOp op) {
4631 if (!isa<verif::ContractOp>(op->getParentOp()))
4632 return lowerVerifIntrinsicOp<verif::AssertOp>(op);
4633 return lowerVerifIntrinsicOp<verif::EnsureOp>(op);
4636LogicalResult FIRRTLLowering::visitExpr(HasBeenResetIntrinsicOp op) {
4637 auto clock = getLoweredNonClockValue(op.getClock());
4638 auto reset = getLoweredValue(op.getReset());
4639 if (!clock || !reset)
4641 auto resetType = op.getReset().getType();
4642 auto uintResetType = dyn_cast<UIntType>(resetType);
4643 auto isSync = uintResetType && uintResetType.getWidth() == 1;
4644 auto isAsync = isa<AsyncResetType>(resetType);
4645 if (!isAsync && !isSync) {
4646 auto d = op.emitError(
"uninferred reset passed to 'has_been_reset'; "
4647 "requires sync or async reset");
4648 d.attachNote() <<
"reset is of type " << resetType
4649 <<
", should be '!firrtl.uint<1>' or '!firrtl.asyncreset'";
4652 return setLoweringTo<verif::HasBeenResetOp>(op, clock, reset, isAsync);
4659LogicalResult FIRRTLLowering::visitExpr(BitsPrimOp op) {
4660 auto input = getLoweredValue(op.getInput());
4664 Type resultType = builder.getIntegerType(op.getHi() - op.getLo() + 1);
4665 return setLoweringTo<comb::ExtractOp>(op, resultType, input, op.getLo());
4668LogicalResult FIRRTLLowering::visitExpr(InvalidValueOp op) {
4669 auto resultTy =
lowerType(op.getType());
4676 if (type_isa<AnalogType>(op.getType()))
4679 return setLoweringTo<sv::WireOp>(op, resultTy,
".invalid_analog");
4682 if (type_cast<FIRRTLBaseType>(op.getType()).containsAnalog())
4693 auto constant = getOrCreateIntConstant(*bitwidth, 0);
4695 if (!type_isa<IntegerType>(resultTy))
4697 return setLowering(op, constant);
4701 op.emitOpError(
"unsupported type");
4705LogicalResult FIRRTLLowering::visitExpr(HeadPrimOp op) {
4706 auto input = getLoweredValue(op.getInput());
4709 auto inWidth = type_cast<IntegerType>(input.getType()).getWidth();
4710 if (op.getAmount() == 0)
4711 return setLowering(op, Value());
4712 Type resultType = builder.getIntegerType(op.getAmount());
4713 return setLoweringTo<comb::ExtractOp>(op, resultType, input,
4714 inWidth - op.getAmount());
4717LogicalResult FIRRTLLowering::visitExpr(ShlPrimOp op) {
4718 auto input = getLoweredValue(op.getInput());
4721 if (op.getAmount() == 0)
4723 return setLowering(op, getOrCreateIntConstant(op.getAmount(), 0));
4728 if (op.getAmount() == 0)
4729 return setLowering(op, input);
4731 auto zero = getOrCreateIntConstant(op.getAmount(), 0);
4732 return setLoweringTo<comb::ConcatOp>(op, input, zero);
4735LogicalResult FIRRTLLowering::visitExpr(ShrPrimOp op) {
4736 auto input = getLoweredValue(op.getInput());
4741 auto inWidth = type_cast<IntegerType>(input.getType()).getWidth();
4742 auto shiftAmount = op.getAmount();
4743 if (shiftAmount >= inWidth) {
4745 if (type_cast<IntType>(op.getInput().getType()).isUnsigned())
4746 return setLowering(op, {});
4749 shiftAmount = inWidth - 1;
4752 Type resultType = builder.getIntegerType(inWidth - shiftAmount);
4753 return setLoweringTo<comb::ExtractOp>(op, resultType, input, shiftAmount);
4756LogicalResult FIRRTLLowering::visitExpr(TailPrimOp op) {
4757 auto input = getLoweredValue(op.getInput());
4761 auto inWidth = type_cast<IntegerType>(input.getType()).getWidth();
4762 if (inWidth == op.getAmount())
4763 return setLowering(op, Value());
4764 Type resultType = builder.getIntegerType(inWidth - op.getAmount());
4765 return setLoweringTo<comb::ExtractOp>(op, resultType, input, 0);
4768LogicalResult FIRRTLLowering::visitExpr(MuxPrimOp op) {
4769 auto cond = getLoweredValue(op.getSel());
4770 auto ifTrue = getLoweredAndExtendedValue(op.getHigh(), op.getType());
4771 auto ifFalse = getLoweredAndExtendedValue(op.getLow(), op.getType());
4772 if (!cond || !ifTrue || !ifFalse)
4775 if (isa<ClockType>(op.getType()))
4776 return setLoweringTo<seq::ClockMuxOp>(op, cond, ifTrue, ifFalse);
4777 return setLoweringTo<comb::MuxOp>(op, ifTrue.getType(), cond, ifTrue, ifFalse,
4781LogicalResult FIRRTLLowering::visitExpr(Mux2CellIntrinsicOp op) {
4782 auto cond = getLoweredValue(op.getSel());
4783 auto ifTrue = getLoweredAndExtendedValue(op.getHigh(), op.getType());
4784 auto ifFalse = getLoweredAndExtendedValue(op.getLow(), op.getType());
4785 if (!cond || !ifTrue || !ifFalse)
4788 auto val = comb::MuxOp::create(builder, ifTrue.getType(), cond, ifTrue,
4790 return setLowering(op, createValueWithMuxAnnotation(val,
true));
4793LogicalResult FIRRTLLowering::visitExpr(Mux4CellIntrinsicOp op) {
4794 auto sel = getLoweredValue(op.getSel());
4795 auto v3 = getLoweredAndExtendedValue(op.getV3(), op.getType());
4796 auto v2 = getLoweredAndExtendedValue(op.getV2(), op.getType());
4797 auto v1 = getLoweredAndExtendedValue(op.getV1(), op.getType());
4798 auto v0 = getLoweredAndExtendedValue(op.getV0(), op.getType());
4799 if (!sel || !v3 || !v2 || !v1 || !v0)
4801 Value array[] = {v3, v2, v1, v0};
4804 return setLowering(op, createValueWithMuxAnnotation(val,
false));
4823Value FIRRTLLowering::createValueWithMuxAnnotation(Operation *op,
bool isMux2) {
4824 assert(op->getNumResults() == 1 &&
"only expect a single result");
4825 auto val = op->getResult(0);
4829 op, sv::SVAttributeAttr::get(builder.getContext(),
"cadence map_to_mux",
4836 OpBuilder::InsertionGuard guard(builder);
4837 builder.setInsertionPoint(op);
4838 StringRef namehint = isMux2 ?
"mux2cell_in" :
"mux4cell_in";
4839 for (
auto [idx, operand] :
llvm::enumerate(op->getOperands())) {
4841 op->getContext(),
nullptr, 0,
4844 hw::WireOp::create(builder, operand, namehint + Twine(idx), innerSym);
4845 op->setOperand(idx, wire);
4850 sv::setSVAttributes(assignOp,
4851 sv::SVAttributeAttr::get(builder.getContext(),
4852 "synopsys infer_mux_override",
4857Value FIRRTLLowering::createArrayIndexing(Value array, Value index) {
4859 auto size = hw::type_cast<hw::ArrayType>(array.getType()).getNumElements();
4864 if (!llvm::isPowerOf2_64(size)) {
4865 auto extElem = getOrCreateIntConstant(APInt(llvm::Log2_64_Ceil(size), 0));
4867 SmallVector<Value> temp(llvm::NextPowerOf2(size) - size, extValue);
4869 Value temp2[] = {ext.getResult(), array};
4875 return inBoundsRead;
4878LogicalResult FIRRTLLowering::visitExpr(MultibitMuxOp op) {
4880 auto index = getLoweredAndExtOrTruncValue(
4882 UIntType::get(op.getContext(),
4887 SmallVector<Value> loweredInputs;
4888 loweredInputs.reserve(op.getInputs().size());
4889 for (
auto input : op.getInputs()) {
4890 auto lowered = getLoweredAndExtendedValue(input, op.getType());
4893 loweredInputs.push_back(lowered);
4897 return setLowering(op, createArrayIndexing(array, index));
4900LogicalResult FIRRTLLowering::visitExpr(VerbatimExprOp op) {
4901 auto resultTy =
lowerType(op.getType());
4905 SmallVector<Value, 4> operands;
4906 operands.reserve(op.getSubstitutions().size());
4907 for (
auto operand : op.getSubstitutions()) {
4908 auto lowered = getLoweredValue(operand);
4911 operands.push_back(lowered);
4914 ArrayAttr symbols = op.getSymbolsAttr();
4916 symbols = ArrayAttr::get(op.getContext(), {});
4918 return setLoweringTo<sv::VerbatimExprOp>(op, resultTy, op.getTextAttr(),
4922LogicalResult FIRRTLLowering::visitExpr(XMRRefOp op) {
4926 Type baseType = op.getType().getType();
4929 if (isa<ClockType>(baseType))
4930 xmrType = builder.getIntegerType(1);
4934 return setLoweringTo<sv::XMRRefOp>(op, sv::InOutType::get(xmrType),
4935 op.getRef(), op.getVerbatimSuffixAttr());
4938LogicalResult FIRRTLLowering::visitExpr(XMRDerefOp op) {
4942 if (isa<ClockType>(op.getType()))
4943 xmrType = builder.getIntegerType(1);
4947 auto xmr = sv::XMRRefOp::create(builder, sv::InOutType::get(xmrType),
4948 op.getRef(), op.getVerbatimSuffixAttr());
4949 auto readXmr = getReadValue(xmr);
4950 if (!isa<ClockType>(op.getType()))
4951 return setLowering(op, readXmr);
4952 return setLoweringTo<seq::ToClockOp>(op, readXmr);
4957LogicalResult FIRRTLLowering::visitExpr(TimeOp op) {
return success(); }
4958LogicalResult FIRRTLLowering::visitExpr(HierarchicalModuleNameOp op) {
4966LogicalResult FIRRTLLowering::visitStmt(SkipOp op) {
4978FailureOr<bool> FIRRTLLowering::lowerConnect(Value destVal, Value srcVal) {
4979 auto srcType = srcVal.getType();
4980 auto dstType = destVal.getType();
4981 if (srcType != dstType &&
4982 (isa<hw::TypeAliasType>(srcType) || isa<hw::TypeAliasType>(dstType))) {
4985 return TypeSwitch<Operation *, FailureOr<bool>>(destVal.getDefiningOp())
4986 .Case<hw::WireOp>([&](
auto op) {
4987 maybeUnused(op.getInput());
4988 op.getInputMutable().assign(srcVal);
4991 .Case<seq::FirRegOp>([&](
auto op) {
4992 maybeUnused(op.getNext());
4993 op.getNextMutable().assign(srcVal);
4996 .Case<hw::StructExtractOp, hw::ArrayGetOp>([](
auto op) {
4999 op.emitOpError(
"used as connect destination");
5002 .Default([](
auto) {
return false; });
5005LogicalResult FIRRTLLowering::visitStmt(ConnectOp op) {
5006 auto dest = op.getDest();
5008 auto destType = type_cast<FIRRTLBaseType>(dest.getType()).getPassiveType();
5009 auto srcVal = getLoweredAndExtendedValue(op.getSrc(), destType);
5011 return handleZeroBit(op.getSrc(), []() { return success(); });
5013 auto destVal = getPossiblyInoutLoweredValue(dest);
5017 auto result = lowerConnect(destVal, srcVal);
5025 if (updateIfBackedge(destVal, srcVal))
5028 if (!isa<hw::InOutType>(destVal.getType()))
5029 return op.emitError(
"destination isn't an inout type");
5035LogicalResult FIRRTLLowering::visitStmt(MatchingConnectOp op) {
5036 auto dest = op.getDest();
5037 auto srcVal = getLoweredValue(op.getSrc());
5039 return handleZeroBit(op.getSrc(), []() { return success(); });
5041 auto destVal = getPossiblyInoutLoweredValue(dest);
5045 auto result = lowerConnect(destVal, srcVal);
5053 if (updateIfBackedge(destVal, srcVal))
5056 if (!isa<hw::InOutType>(destVal.getType()))
5057 return op.emitError(
"destination isn't an inout type");
5063LogicalResult FIRRTLLowering::visitStmt(ForceOp op) {
5064 auto srcVal = getLoweredValue(op.getSrc());
5068 auto destVal = getPossiblyInoutLoweredValue(op.getDest());
5072 if (!isa<hw::InOutType>(destVal.getType()))
5073 return op.emitError(
"destination isn't an inout type");
5076 circuitState.addMacroDecl(builder.getStringAttr(
"SYNTHESIS"));
5077 addToIfDefBlock(
"SYNTHESIS", std::function<
void()>(), [&]() {
5078 addToInitialBlock([&]() { sv::ForceOp::create(builder, destVal, srcVal); });
5083LogicalResult FIRRTLLowering::visitStmt(RefForceOp op) {
5084 auto src = getLoweredNonClockValue(op.getSrc());
5085 auto clock = getLoweredNonClockValue(op.getClock());
5086 auto pred = getLoweredValue(op.getPredicate());
5087 if (!src || !clock || !pred)
5090 auto destVal = getPossiblyInoutLoweredValue(op.getDest());
5095 circuitState.addMacroDecl(builder.getStringAttr(
"SYNTHESIS"));
5096 addToIfDefBlock(
"SYNTHESIS", std::function<
void()>(), [&]() {
5097 addToAlwaysBlock(clock, [&]() {
5098 addIfProceduralBlock(
5099 pred, [&]() { sv::ForceOp::create(builder, destVal, src); });
5104LogicalResult FIRRTLLowering::visitStmt(RefForceInitialOp op) {
5105 auto src = getLoweredNonClockValue(op.getSrc());
5106 auto pred = getLoweredValue(op.getPredicate());
5110 auto destVal = getPossiblyInoutLoweredValue(op.getDest());
5115 circuitState.addMacroDecl(builder.getStringAttr(
"SYNTHESIS"));
5116 addToIfDefBlock(
"SYNTHESIS", std::function<
void()>(), [&]() {
5117 addToInitialBlock([&]() {
5118 addIfProceduralBlock(
5119 pred, [&]() { sv::ForceOp::create(builder, destVal, src); });
5124LogicalResult FIRRTLLowering::visitStmt(RefReleaseOp op) {
5125 auto clock = getLoweredNonClockValue(op.getClock());
5126 auto pred = getLoweredValue(op.getPredicate());
5127 if (!clock || !pred)
5130 auto destVal = getPossiblyInoutLoweredValue(op.getDest());
5135 circuitState.addMacroDecl(builder.getStringAttr(
"SYNTHESIS"));
5136 addToIfDefBlock(
"SYNTHESIS", std::function<
void()>(), [&]() {
5137 addToAlwaysBlock(clock, [&]() {
5138 addIfProceduralBlock(pred,
5139 [&]() { sv::ReleaseOp::create(builder, destVal); });
5144LogicalResult FIRRTLLowering::visitStmt(RefReleaseInitialOp op) {
5145 auto destVal = getPossiblyInoutLoweredValue(op.getDest());
5146 auto pred = getLoweredValue(op.getPredicate());
5147 if (!destVal || !pred)
5151 circuitState.addMacroDecl(builder.getStringAttr(
"SYNTHESIS"));
5152 addToIfDefBlock(
"SYNTHESIS", std::function<
void()>(), [&]() {
5153 addToInitialBlock([&]() {
5154 addIfProceduralBlock(pred,
5155 [&]() { sv::ReleaseOp::create(builder, destVal); });
5163 StringRef originalFormatString,
5164 ValueRange operands,
5165 StringAttr &result) {
5168 SmallString<32> formatString;
5169 for (
size_t i = 0, e = originalFormatString.size(), subIdx = 0; i != e; ++i) {
5170 char c = originalFormatString[i];
5174 formatString.push_back(c);
5177 SmallString<6> width;
5178 c = originalFormatString[++i];
5181 c = originalFormatString[++i];
5192 formatString.append(width);
5198 formatString.push_back(c);
5205 if (originalFormatString.slice(i, i + 4) !=
"{{}}") {
5206 formatString.push_back(c);
5210 auto substitution = operands[subIdx++];
5211 assert(type_isa<FStringType>(substitution.getType()) &&
5212 "the operand for a '{{}}' substitution must be an 'fstring' type");
5214 TypeSwitch<Operation *, LogicalResult>(substitution.getDefiningOp())
5215 .template Case<TimeOp>([&](
auto) {
5216 formatString.append(
"%0t");
5219 .
template Case<HierarchicalModuleNameOp>([&](
auto) {
5220 formatString.append(
"%m");
5223 .Default([&](
auto) {
5224 emitError(loc,
"has a substitution with an unimplemented "
5226 .attachNote(substitution.getLoc())
5227 <<
"op with an unimplemented lowering is here";
5237 formatString.push_back(c);
5241 result = StringAttr::get(loc->getContext(), formatString);
5248LogicalResult FIRRTLLowering::visitPrintfLike(
5249 T op,
const FileDescriptorInfo &fileDescriptorInfo,
bool usePrintfCond) {
5250 auto clock = getLoweredNonClockValue(op.getClock());
5251 auto cond = getLoweredValue(op.getCond());
5252 if (!clock || !cond)
5255 StringAttr formatString;
5257 op.getSubstitutions(), formatString)))
5260 auto fn = [&](Value fd) {
5261 SmallVector<Value> operands;
5262 if (failed(loweredFmtOperands(op.getSubstitutions(), operands)))
5264 sv::FWriteOp::create(builder, op.getLoc(), fd, formatString, operands);
5268 return lowerStatementWithFd(fileDescriptorInfo, clock, cond, fn,
5272LogicalResult FIRRTLLowering::visitStmt(FPrintFOp op) {
5273 StringAttr outputFileAttr;
5275 op.getOutputFileSubstitutions(),
5279 FileDescriptorInfo outputFile(outputFileAttr,
5280 op.getOutputFileSubstitutions());
5281 return visitPrintfLike(op, outputFile,
false);
5285LogicalResult FIRRTLLowering::visitStmt(FFlushOp op) {
5286 auto clock = getLoweredNonClockValue(op.getClock());
5287 auto cond = getLoweredValue(op.getCond());
5288 if (!clock || !cond)
5291 auto fn = [&](Value fd) {
5292 sv::FFlushOp::create(builder, op.getLoc(), fd);
5296 if (!op.getOutputFileAttr())
5297 return lowerStatementWithFd({}, clock, cond, fn,
false);
5301 StringAttr outputFileAttr;
5303 op.getOutputFileSubstitutions(),
5307 return lowerStatementWithFd(
5308 FileDescriptorInfo(outputFileAttr, op.getOutputFileSubstitutions()),
5309 clock, cond, fn,
false);
5314LogicalResult FIRRTLLowering::visitStmt(StopOp op) {
5315 auto clock = getLoweredValue(op.getClock());
5316 auto cond = getLoweredValue(op.getCond());
5317 if (!clock || !cond)
5320 circuitState.usedStopCond =
true;
5321 circuitState.addFragment(theModule,
"STOP_COND_FRAGMENT");
5324 sv::MacroRefExprOp::create(builder, cond.getType(),
"STOP_COND_");
5325 Value exitCond = builder.createOrFold<
comb::AndOp>(stopCond, cond,
true);
5327 sim::ClockedTerminateOp::create(builder, clock, exitCond,
5328 op.getExitCode() == 0,
5337template <
typename... Args>
5339 StringRef opName, Args &&...args) {
5340 if (opName ==
"assert")
5341 return sv::AssertOp::create(builder, std::forward<Args>(args)...);
5342 if (opName ==
"assume")
5343 return sv::AssumeOp::create(builder, std::forward<Args>(args)...);
5344 if (opName ==
"cover")
5345 return sv::CoverOp::create(builder, std::forward<Args>(args)...);
5346 llvm_unreachable(
"unknown verification op");
5352template <
typename... Args>
5354 StringRef opName, Args &&...args) {
5355 if (opName ==
"assert")
5356 return sv::AssertConcurrentOp::create(builder, std::forward<Args>(args)...);
5357 if (opName ==
"assume")
5358 return sv::AssumeConcurrentOp::create(builder, std::forward<Args>(args)...);
5359 if (opName ==
"cover")
5360 return sv::CoverConcurrentOp::create(builder, std::forward<Args>(args)...);
5361 llvm_unreachable(
"unknown verification op");
5382LogicalResult FIRRTLLowering::lowerVerificationStatement(
5383 Operation *op, StringRef labelPrefix, Value opClock, Value opPredicate,
5384 Value opEnable, StringAttr opMessageAttr, ValueRange opOperands,
5385 StringAttr opNameAttr,
bool isConcurrent, EventControl opEventControl) {
5386 StringRef opName = op->getName().stripDialect();
5389 ArrayRef<Attribute> guards{};
5390 if (
auto guardsAttr = op->template getAttrOfType<ArrayAttr>(
"guards"))
5391 guards = guardsAttr.getValue();
5393 auto isCover = isa<CoverOp>(op);
5394 auto clock = getLoweredNonClockValue(opClock);
5395 auto enable = getLoweredValue(opEnable);
5396 auto predicate = getLoweredValue(opPredicate);
5397 if (!clock || !enable || !predicate)
5401 if (opNameAttr && !opNameAttr.getValue().empty())
5403 StringAttr prefixedLabel;
5406 StringAttr::get(builder.getContext(), labelPrefix + label.getValue());
5409 SmallVector<Value> messageOps;
5413 if (flavor == VerificationFlavor::IfElseFatal && !isa<AssertOp>(op))
5414 flavor = VerificationFlavor::None;
5416 if (flavor == VerificationFlavor::None) {
5420 auto format = op->getAttrOfType<StringAttr>(
"format");
5422 if (isConcurrent && format && format.getValue() ==
"ifElseFatal") {
5423 if (!isa<AssertOp>(op))
5424 return op->emitError()
5425 <<
"ifElseFatal format cannot be used for non-assertions";
5426 flavor = VerificationFlavor::IfElseFatal;
5427 }
else if (isConcurrent)
5428 flavor = VerificationFlavor::SVA;
5430 flavor = VerificationFlavor::Immediate;
5433 if (!isCover && opMessageAttr && !opMessageAttr.getValue().empty()) {
5437 opOperands, message)))
5440 if (failed(loweredFmtOperands(opOperands, messageOps)))
5443 if (flavor == VerificationFlavor::SVA) {
5448 for (
auto &loweredValue : messageOps)
5449 loweredValue =
sv::SampledOp::create(builder, loweredValue);
5455 case VerificationFlavor::Immediate: {
5457 auto deferImmediate = circt::sv::DeferAssertAttr::get(
5458 builder.getContext(), circt::sv::DeferAssert::Immediate);
5459 addToAlwaysBlock(clock, [&]() {
5460 addIfProceduralBlock(enable, [&]() {
5462 prefixedLabel, message, messageOps);
5467 case VerificationFlavor::IfElseFatal: {
5468 assert(isa<AssertOp>(op) &&
"only assert is expected");
5471 auto boolType = IntegerType::get(builder.getContext(), 1);
5472 predicate = comb::createOrFoldNot(builder, predicate,
true);
5473 predicate = builder.createOrFold<
comb::AndOp>(enable, predicate,
true);
5475 circuitState.addMacroDecl(builder.getStringAttr(
"SYNTHESIS"));
5476 addToIfDefBlock(
"SYNTHESIS", {}, [&]() {
5477 addToAlwaysBlock(clock, [&]() {
5478 addIfProceduralBlock(predicate, [&]() {
5479 circuitState.usedStopCond =
true;
5480 circuitState.addFragment(theModule,
"STOP_COND_FRAGMENT");
5482 circuitState.usedAssertVerboseCond =
true;
5483 circuitState.addFragment(theModule,
"ASSERT_VERBOSE_COND_FRAGMENT");
5485 addIfProceduralBlock(
5486 sv::MacroRefExprOp::create(builder, boolType,
5487 "ASSERT_VERBOSE_COND_"),
5489 sv::ErrorProceduralOp::create(builder, message, messageOps);
5491 addIfProceduralBlock(
5492 sv::MacroRefExprOp::create(builder, boolType,
"STOP_COND_"),
5493 [&]() { sv::FatalProceduralOp::create(builder); });
5499 case VerificationFlavor::SVA: {
5504 comb::createOrFoldNot(builder, enable,
true);
5506 builder.createOrFold<
comb::OrOp>(notEnable, predicate,
true);
5508 predicate = builder.createOrFold<
comb::AndOp>(enable, predicate,
true);
5512 sv::EventControl event;
5513 switch (opEventControl) {
5514 case EventControl::AtPosEdge:
5515 event = circt::sv::EventControl::AtPosEdge;
5517 case EventControl::AtEdge:
5518 event = circt::sv::EventControl::AtEdge;
5520 case EventControl::AtNegEdge:
5521 event = circt::sv::EventControl::AtNegEdge;
5527 circt::sv::EventControlAttr::get(builder.getContext(), event), clock,
5528 predicate, prefixedLabel, message, messageOps);
5531 case VerificationFlavor::None:
5533 "flavor `None` must be converted into one of concreate flavors");
5540 return emitGuards(op->getLoc(), guards,
emit);
5544LogicalResult FIRRTLLowering::visitStmt(AssertOp op) {
5545 return lowerVerificationStatement(
5546 op,
"assert__", op.getClock(), op.getPredicate(), op.getEnable(),
5547 op.getMessageAttr(), op.getSubstitutions(), op.getNameAttr(),
5548 op.getIsConcurrent(), op.getEventControl());
5552LogicalResult FIRRTLLowering::visitStmt(AssumeOp op) {
5553 return lowerVerificationStatement(
5554 op,
"assume__", op.getClock(), op.getPredicate(), op.getEnable(),
5555 op.getMessageAttr(), op.getSubstitutions(), op.getNameAttr(),
5556 op.getIsConcurrent(), op.getEventControl());
5560LogicalResult FIRRTLLowering::visitStmt(CoverOp op) {
5561 return lowerVerificationStatement(
5562 op,
"cover__", op.getClock(), op.getPredicate(), op.getEnable(),
5563 op.getMessageAttr(), op.getSubstitutions(), op.getNameAttr(),
5564 op.getIsConcurrent(), op.getEventControl());
5568LogicalResult FIRRTLLowering::visitStmt(UnclockedAssumeIntrinsicOp op) {
5573 auto guardsAttr = op->getAttrOfType<mlir::ArrayAttr>(
"guards");
5574 ArrayRef<Attribute> guards =
5575 guardsAttr ? guardsAttr.getValue() : ArrayRef<Attribute>();
5577 auto label = op.getNameAttr();
5578 StringAttr assumeLabel;
5579 if (label && !label.empty())
5581 StringAttr::get(builder.getContext(),
"assume__" + label.getValue());
5582 auto predicate = getLoweredValue(op.getPredicate());
5583 auto enable = getLoweredValue(op.getEnable());
5584 auto notEnable = comb::createOrFoldNot(builder, enable,
true);
5585 predicate = builder.createOrFold<
comb::OrOp>(notEnable, predicate,
true);
5587 SmallVector<Value> messageOps;
5588 for (
auto operand : op.getSubstitutions()) {
5589 auto loweredValue = getLoweredValue(operand);
5590 if (!loweredValue) {
5594 loweredValue = getOrCreateIntConstant(1, 0);
5596 messageOps.push_back(loweredValue);
5598 return emitGuards(op.getLoc(), guards, [&]() {
5599 sv::AlwaysOp::create(
5600 builder, ArrayRef(sv::EventControl::AtEdge), ArrayRef(predicate),
5602 if (op.getMessageAttr().getValue().empty())
5603 buildImmediateVerifOp(
5604 builder,
"assume", predicate,
5605 circt::sv::DeferAssertAttr::get(
5606 builder.getContext(), circt::sv::DeferAssert::Immediate),
5609 buildImmediateVerifOp(
5610 builder,
"assume", predicate,
5611 circt::sv::DeferAssertAttr::get(
5612 builder.getContext(), circt::sv::DeferAssert::Immediate),
5613 assumeLabel, op.getMessageAttr(), messageOps);
5618LogicalResult FIRRTLLowering::visitStmt(AttachOp op) {
5620 if (op.getAttached().size() < 2)
5623 SmallVector<Value, 4> inoutValues;
5624 for (
auto v : op.getAttached()) {
5625 inoutValues.push_back(getPossiblyInoutLoweredValue(v));
5626 if (!inoutValues.back()) {
5630 inoutValues.pop_back();
5634 if (!isa<hw::InOutType>(inoutValues.back().getType()))
5635 return op.emitError(
"operand isn't an inout type");
5638 if (inoutValues.size() < 2)
5649 bool isAttachInternalOnly =
5650 llvm::none_of(inoutValues, [](
auto v) {
return isa<BlockArgument>(v); });
5652 if (isAttachInternalOnly) {
5653 auto v0 = inoutValues.front();
5654 for (
auto v : inoutValues) {
5657 v.replaceAllUsesWith(v0);
5664 circuitState.addMacroDecl(builder.getStringAttr(
"SYNTHESIS"));
5665 circuitState.addMacroDecl(builder.getStringAttr(
"VERILATOR"));
5670 SmallVector<Value, 4> values;
5671 for (
auto inoutValue : inoutValues)
5672 values.push_back(getReadValue(inoutValue));
5674 for (
size_t i1 = 0, e = inoutValues.size(); i1 != e; ++i1) {
5675 for (
size_t i2 = 0; i2 != e; ++i2)
5683 sv::IfDefOp::create(
5684 builder,
"VERILATOR",
5686 sv::VerbatimOp::create(
5688 "`error \"Verilator does not support alias and thus "
5690 "arbitrarily connect bidirectional wires and ports\"");
5692 [&]() { sv::AliasOp::create(builder, inoutValues); });
5698LogicalResult FIRRTLLowering::visitStmt(BindOp op) {
5699 sv::BindOp::create(builder, op.getInstanceAttr());
5703LogicalResult FIRRTLLowering::fixupLTLOps() {
5704 if (ltlOpFixupWorklist.empty())
5706 LLVM_DEBUG(llvm::dbgs() <<
"Fixing up " << ltlOpFixupWorklist.size()
5710 for (
unsigned i = 0, e = ltlOpFixupWorklist.size(); i != e; ++i)
5711 for (
auto *user : ltlOpFixupWorklist[i]->getUsers())
5712 if (isa<
hw::WireOp>(user))
5713 ltlOpFixupWorklist.insert(user);
5716 while (!ltlOpFixupWorklist.empty()) {
5717 auto *op = ltlOpFixupWorklist.pop_back_val();
5720 if (
auto opIntf = dyn_cast_or_null<mlir::InferTypeOpInterface>(op)) {
5721 LLVM_DEBUG(llvm::dbgs() <<
"- Update " << *op <<
"\n");
5722 SmallVector<Type, 2> types;
5723 auto result = opIntf.inferReturnTypes(
5724 op->getContext(), op->getLoc(), op->getOperands(),
5725 op->getAttrDictionary(), op->getPropertiesStorage(), op->getRegions(),
5729 assert(types.size() == op->getNumResults());
5733 for (
auto [result, type] :
llvm::zip(op->getResults(), types)) {
5734 if (result.getType() == type)
5736 LLVM_DEBUG(llvm::dbgs()
5737 <<
" - Result #" << result.getResultNumber() <<
" from "
5738 << result.getType() <<
" to " << type <<
"\n");
5739 result.setType(type);
5740 for (
auto *user : result.getUsers())
5742 ltlOpFixupWorklist.insert(user);
5747 if (
auto wireOp = dyn_cast<hw::WireOp>(op)) {
5748 if (isa<ltl::SequenceType, ltl::PropertyType>(wireOp.getType())) {
5749 wireOp.replaceAllUsesWith(wireOp.getInput());
5750 LLVM_DEBUG(llvm::dbgs() <<
"- Remove " << wireOp <<
"\n");
5751 if (wireOp.use_empty())
5758 SmallPtrSet<Operation *, 4> usersReported;
5759 for (
auto *user : op->getUsers()) {
5760 if (!usersReported.insert(user).second)
5762 if (isa_and_nonnull<ltl::LTLDialect, verif::VerifDialect>(
5763 user->getDialect()))
5765 if (isa<hw::WireOp>(user))
5767 auto d = op->emitError(
5768 "verification operation used in a non-verification context");
5769 d.attachNote(user->getLoc())
5770 <<
"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 void moveVerifAnno(ModuleOp top, AnnotationSet &annos, StringRef annoClass, StringRef attrBase)
Move a ExtractTestCode related annotation from annotations to an attribute.
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 Block * getBodyBlock(FModuleLike mod)
std::shared_ptr< calyx::CalyxLoweringState > loweringState
Instantiate one of these and use it to build typed backedges.
void abandon()
Abandon the backedges, suppressing any diagnostics if they are still active upon destruction of the b...
Backedge get(mlir::Type resultType, mlir::LocationAttr optionalLoc={})
Create a typed backedge.
mlir::LogicalResult clearOrEmitError()
Clear the backedges, erasing any remaining cursor ops.
Backedge is a wrapper class around a Value.
void setValue(mlir::Value)
A namespace that is used to store existing names and generate new names in some scope within the IR.
This class provides a read-only projection over the MLIR attributes that represent a set of annotatio...
bool removeAnnotations(llvm::function_ref< bool(Annotation)> predicate)
Remove all annotations from this annotation set for which predicate returns true.
bool removeAnnotation(Annotation anno)
Remove an annotation from this annotation set.
Annotation getAnnotation(StringRef className) const
If this annotation set has an annotation with the specified class name, return it.
This class provides a read-only projection of an annotation.
DictionaryAttr getDict() const
Get the data dictionary of this attribute.
AttrClass getMember(StringAttr name) const
Return a member of the annotation.
bool isClass(Args... names) const
Return true if this annotation matches any of the specified class names.
FIRRTLVisitor allows you to visit all of the expr/stmt/decls with one class declaration.
ResultType visitInvalidOp(Operation *op, ExtraArgs... args)
visitInvalidOp is an override point for non-FIRRTL dialect operations.
ResultType visitUnhandledOp(Operation *op, ExtraArgs... args)
visitUnhandledOp is an override point for FIRRTL dialect ops that the concrete visitor didn't bother ...
This graph tracks modules and where they are instantiated.
FModuleLike getTopLevelModule()
Get the module corresponding to the top-level module of a circuit.
This is the common base class between SIntType and UIntType.
This table tracks nlas and what modules participate in them.
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