35#include "mlir/IR/BuiltinOps.h"
36#include "mlir/IR/BuiltinTypes.h"
37#include "mlir/IR/ImplicitLocOpBuilder.h"
38#include "mlir/IR/Threading.h"
39#include "mlir/Pass/Pass.h"
40#include "llvm/Support/Debug.h"
41#include "llvm/Support/Mutex.h"
43#define DEBUG_TYPE "lower-to-hw"
46#define GEN_PASS_DEF_LOWERFIRRTLTOHW
47#include "circt/Conversion/Passes.h.inc"
51using namespace firrtl;
52using circt::comb::ICmpPredicate;
61 auto ftype = dyn_cast<FIRRTLBaseType>(type);
62 return ftype && ftype.getPassiveType().getBitWidthOrSentinel() == 0;
69 for (
auto operand : op.getAttached()) {
71 operand.getDefiningOp<InstanceOp>())
75 if (!operand.hasOneUse() || singleSource)
77 singleSource = operand;
86 auto checkTypes = [](Operation *op) -> WalkResult {
88 if (isa_and_nonnull<FIRRTLDialect>(op->getDialect()))
89 return op->emitError(
"Found unhandled FIRRTL operation '")
90 << op->getName() <<
"'";
93 auto checkTypeRange = [&](TypeRange types) -> LogicalResult {
94 if (llvm::any_of(types, [](Type type) {
95 return isa<FIRRTLDialect>(type.getDialect());
97 return op->emitOpError(
"found unhandled FIRRTL type");
102 if (failed(checkTypeRange(op->getOperandTypes())) ||
103 failed(checkTypeRange(op->getResultTypes())))
104 return WalkResult::interrupt();
107 for (
auto ®ion : op->getRegions())
108 for (
auto &block : region)
109 if (failed(checkTypeRange(block.getArgumentTypes())))
110 return WalkResult::interrupt();
113 return WalkResult::advance();
116 if (checkTypes(op).wasInterrupted() || op->walk(checkTypes).wasInterrupted())
123 auto t1c = type_cast<IntType>(t1), t2c = type_cast<IntType>(t2);
124 return t2c.getWidth() > t1c.getWidth() ? t2c : t1c;
130 ImplicitLocOpBuilder &builder) {
132 if (BundleType bundle = dyn_cast<BundleType>(type))
133 val = builder.createOrFold<HWStructCastOp>(bundle.getPassiveType(), val);
135 if (type != val.getType())
136 val = mlir::UnrealizedConversionCastOp::create(builder, type, val)
144 ImplicitLocOpBuilder &builder) {
146 if (hw::StructType structTy = dyn_cast<hw::StructType>(type)) {
148 val = mlir::UnrealizedConversionCastOp::create(
150 type_cast<FIRRTLBaseType>(val.getType()).getPassiveType(), val)
152 val = builder.createOrFold<HWStructCastOp>(type, val);
157 mlir::UnrealizedConversionCastOp::create(builder, type, val).getResult(0);
164 StringRef annoClass, StringRef attrBase) {
166 auto *ctx = top.getContext();
169 if (
auto dir = anno.getMember<StringAttr>(
"directory")) {
170 SmallVector<NamedAttribute> old;
171 for (
auto i : top->getAttrs())
174 StringAttr::get(ctx, attrBase),
175 hw::OutputFileAttr::getAsDirectory(ctx, dir.getValue(),
true,
true));
178 if (
auto file = anno.getMember<StringAttr>(
"filename")) {
179 SmallVector<NamedAttribute> old;
180 for (
auto i : top->getAttrs())
182 old.emplace_back(StringAttr::get(ctx, attrBase +
".bindfile"),
183 hw::OutputFileAttr::getFromFilename(
184 ctx, file.getValue(),
true));
190 return size == 1 ? 1 : llvm::Log2_64_Ceil(size);
196 if (
auto attr = src->getAttrOfType<StringAttr>(
"name"))
197 if (!dst->hasAttr(
"sv.namehint") && !dst->hasAttr(
"name"))
198 dst->setAttr(
"sv.namehint", attr);
204class FileDescriptorInfo {
206 FileDescriptorInfo(StringAttr outputFileName, mlir::ValueRange substitutions)
207 : outputFileFormat(outputFileName), substitutions(substitutions) {
209 substitutions.empty() &&
210 "substitutions must be empty when output file name is empty");
213 FileDescriptorInfo() =
default;
216 bool isSubstitutionRequired()
const {
return !substitutions.empty(); }
219 bool isDefaultFd()
const {
return !outputFileFormat; }
221 StringAttr getOutputFileFormat()
const {
return outputFileFormat; }
222 mlir::ValueRange getSubstitutions()
const {
return substitutions; }
226 StringAttr outputFileFormat = {};
229 mlir::ValueRange substitutions;
239struct FIRRTLModuleLowering;
242struct CircuitLoweringState {
244 std::atomic<bool> usedPrintf{
false};
245 std::atomic<bool> usedAssertVerboseCond{
false};
246 std::atomic<bool> usedStopCond{
false};
247 std::atomic<bool> usedFileDescriptorLib{
false};
249 CircuitLoweringState(CircuitOp circuitOp,
bool enableAnnotationWarning,
252 : circuitOp(circuitOp), instanceGraph(instanceGraph),
253 enableAnnotationWarning(enableAnnotationWarning),
254 verificationFlavor(verificationFlavor), nlaTable(nlaTable) {
255 auto *context = circuitOp.getContext();
260 auto dirName = tbAnno.getMember<StringAttr>(
"dirname");
261 testBenchDirectory = hw::OutputFileAttr::getAsDirectory(
262 context, dirName.getValue(),
false,
true);
266 if (
auto module = dyn_cast<FModuleLike>(op))
277 testHarness =
nullptr;
278 }
else if (dut == testHarness) {
279 testHarness =
nullptr;
284 auto inDUT = [&](igraph::ModuleOpInterface child) {
286 if (
auto inst = instRec->getInstance<InstanceOp>())
287 return inst.getLowerToBind() || inst.getDoNotPrint();
290 if (
auto parent = dyn_cast<igraph::ModuleOpInterface>(*dut))
291 return getInstanceGraph().isAncestor(child, parent, isPhony);
294 circuitOp->walk([&](FModuleLike moduleOp) {
296 dutModules.insert(moduleOp);
300 Operation *getNewModule(Operation *oldModule) {
301 auto it = oldToNewModuleMap.find(oldModule);
302 return it != oldToNewModuleMap.end() ? it->second :
nullptr;
305 Operation *getOldModule(Operation *newModule) {
306 auto it = newToOldModuleMap.find(newModule);
307 return it != newToOldModuleMap.end() ? it->second :
nullptr;
310 void recordModuleMapping(Operation *oldFMod, Operation *newHWMod) {
311 oldToNewModuleMap[oldFMod] = newHWMod;
312 newToOldModuleMap[newHWMod] = oldFMod;
317 void processRemainingAnnotations(Operation *op,
const AnnotationSet &annoSet);
323 void addBind(sv::BindOp op) {
324 std::lock_guard<std::mutex> lock(bindsMutex);
330 hw::TypeAliasType getTypeAlias(Type rawType, BaseTypeAliasType firAliasType,
333 auto hwAlias = typeAliases.getTypedecl(firAliasType);
336 assert(!typeAliases.isFrozen() &&
337 "type aliases cannot be generated after its frozen");
338 return typeAliases.addTypedecl(rawType, firAliasType, typeLoc);
341 FModuleLike getDut() {
return dut; }
342 FModuleLike getTestHarness() {
return testHarness; }
348 bool isInDUT(igraph::ModuleOpInterface child) {
349 if (
auto hwModule = dyn_cast<hw::HWModuleOp>(child.getOperation()))
350 child = cast<igraph::ModuleOpInterface>(getOldModule(hwModule));
351 return dutModules.contains(child);
354 hw::OutputFileAttr getTestBenchDirectory() {
return testBenchDirectory; }
359 bool isInTestHarness(igraph::ModuleOpInterface mod) {
return !isInDUT(mod); }
366 Type
lowerType(Type type, Location loc) {
367 return ::lowerType(type, loc,
368 [&](Type rawType, BaseTypeAliasType firrtlType,
369 Location typeLoc) -> hw::TypeAliasType {
370 return getTypeAlias(rawType, firrtlType, typeLoc);
375 friend struct FIRRTLModuleLowering;
376 friend struct FIRRTLLowering;
377 CircuitLoweringState(
const CircuitLoweringState &) =
delete;
378 void operator=(
const CircuitLoweringState &) =
delete;
381 DenseMap<Operation *, Operation *> oldToNewModuleMap;
384 DenseMap<Operation *, Operation *> newToOldModuleMap;
395 DenseSet<igraph::ModuleOpInterface> dutModules;
399 StringSet<> pendingAnnotations;
400 const bool enableAnnotationWarning;
401 std::mutex annotationPrintingMtx;
407 SmallVector<sv::BindOp> binds;
410 std::mutex bindsMutex;
418 FModuleLike testHarness;
421 hw::OutputFileAttr testBenchDirectory;
425 DenseMap<std::pair<Attribute, Attribute>, Attribute> instanceForceNames;
428 SetVector<StringAttr> macroDeclNames;
429 std::mutex macroDeclMutex;
431 void addMacroDecl(StringAttr name) {
432 std::unique_lock<std::mutex> lock(macroDeclMutex);
433 macroDeclNames.insert(name);
438 DenseMap<hw::HWModuleOp, SetVector<Attribute>> fragments;
439 llvm::sys::SmartMutex<true> fragmentsMutex;
442 llvm::sys::SmartScopedLock<true> lock(fragmentsMutex);
443 fragments[module].insert(
444 FlatSymbolRefAttr::get(circuitOp.getContext(), fragment));
459 struct RecordTypeAlias {
461 RecordTypeAlias(CircuitOp c) : circuitOp(c) {}
463 hw::TypeAliasType getTypedecl(BaseTypeAliasType firAlias)
const {
464 auto iter = firrtlTypeToAliasTypeMap.find(firAlias);
465 if (iter != firrtlTypeToAliasTypeMap.end())
470 bool isFrozen() {
return frozen; }
472 void freeze() { frozen =
true; }
474 hw::TypeAliasType addTypedecl(Type rawType, BaseTypeAliasType firAlias,
476 assert(!frozen &&
"Record already frozen, cannot be updated");
479 auto b = ImplicitLocOpBuilder::atBlockBegin(
481 &circuitOp->getParentRegion()->getBlocks().back());
483 b, b.getStringAttr(circuitOp.getName() +
"__TYPESCOPE_"));
484 typeScope.getBodyRegion().push_back(
new Block());
486 auto typeName = firAlias.getName();
491 StringAttr::get(typeName.getContext(),
492 typeDeclNamespace.newName(typeName.getValue()));
494 auto typeScopeBuilder =
495 ImplicitLocOpBuilder::atBlockEnd(typeLoc, typeScope.getBodyBlock());
497 typeName, rawType,
nullptr);
498 auto hwAlias = hw::TypeAliasType::get(
499 SymbolRefAttr::get(typeScope.getSymNameAttr(),
500 {FlatSymbolRefAttr::get(typeDecl)}),
502 auto insert = firrtlTypeToAliasTypeMap.try_emplace(firAlias, hwAlias);
503 assert(insert.second &&
"Entry already exists, insert failed");
504 return insert.first->second;
513 DenseMap<Type, hw::TypeAliasType> firrtlTypeToAliasTypeMap;
521 RecordTypeAlias typeAliases = RecordTypeAlias(circuitOp);
524void CircuitLoweringState::processRemainingAnnotations(
526 if (!enableAnnotationWarning || annoSet.
empty())
528 std::lock_guard<std::mutex> lock(annotationPrintingMtx);
530 for (
auto a : annoSet) {
531 auto inserted = pendingAnnotations.insert(a.getClass());
532 if (!inserted.second)
571 mlir::emitWarning(op->getLoc(),
"unprocessed annotation:'" + a.getClass() +
572 "' still remaining after LowerToHW");
578struct FIRRTLModuleLowering
579 :
public circt::impl::LowerFIRRTLToHWBase<FIRRTLModuleLowering> {
581 void runOnOperation()
override;
582 void setEnableAnnotationWarning() { enableAnnotationWarning =
true; }
584 using LowerFIRRTLToHWBase<FIRRTLModuleLowering>::verificationFlavor;
587 void lowerFileHeader(CircuitOp op, CircuitLoweringState &
loweringState);
588 LogicalResult lowerPorts(ArrayRef<PortInfo> firrtlPorts,
589 SmallVectorImpl<hw::PortInfo> &ports,
590 Operation *moduleOp, StringRef moduleName,
592 bool handleForceNameAnnos(FModuleLike oldModule,
AnnotationSet &annos,
594 hw::HWModuleOp lowerModule(FModuleOp oldModule, Block *topLevelModule,
597 Block *topLevelModule,
600 Block *topLevelModule,
604 lowerModulePortsAndMoveBody(FModuleOp oldModule,
hw::HWModuleOp newModule,
608 LogicalResult lowerFormalBody(verif::FormalOp formalOp,
610 LogicalResult lowerSimulationBody(verif::SimulationOp simulationOp,
612 LogicalResult lowerFileBody(emit::FileOp op);
620 bool enableAnnotationWarning,
622 auto pass = std::make_unique<FIRRTLModuleLowering>();
623 if (enableAnnotationWarning)
624 pass->setEnableAnnotationWarning();
625 pass->verificationFlavor = verificationFlavor;
631void FIRRTLModuleLowering::runOnOperation() {
635 auto *topLevelModule = getOperation().getBody();
639 for (
auto &op : *topLevelModule) {
640 if ((circuit = dyn_cast<CircuitOp>(&op)))
647 auto *circuitBody = circuit.getBodyBlock();
651 CircuitLoweringState state(circuit, enableAnnotationWarning,
652 verificationFlavor, getAnalysis<InstanceGraph>(),
653 &getAnalysis<NLATable>());
655 SmallVector<Operation *, 32> opsToProcess;
659 "firrtl.extract.assert");
661 "firrtl.extract.assume");
663 "firrtl.extract.cover");
664 circuitAnno.removeAnnotationsWithClass(
667 state.processRemainingAnnotations(circuit, circuitAnno);
670 for (
auto &op : make_early_inc_range(circuitBody->getOperations())) {
672 TypeSwitch<Operation *, LogicalResult>(&op)
673 .Case<FModuleOp>([&](
auto module) {
674 auto loweredMod = lowerModule(module, topLevelModule, state);
678 state.recordModuleMapping(&op, loweredMod);
679 opsToProcess.push_back(loweredMod);
681 module.walk([&](Operation *op) {
682 for (auto res : op->getResults()) {
684 type_dyn_cast<BaseTypeAliasType>(res.getType()))
685 state.lowerType(aliasType, op->getLoc());
688 return lowerModulePortsAndMoveBody(module, loweredMod, state);
690 .Case<FExtModuleOp>([&](
auto extModule) {
692 lowerExtModule(extModule, topLevelModule, state);
695 state.recordModuleMapping(&op, loweredMod);
698 .Case<FMemModuleOp>([&](
auto memModule) {
700 lowerMemModule(memModule, topLevelModule, state);
703 state.recordModuleMapping(&op, loweredMod);
706 .Case<FormalOp>([&](
auto oldOp) {
707 auto builder = OpBuilder::atBlockEnd(topLevelModule);
708 auto newOp = verif::FormalOp::create(builder, oldOp.getLoc(),
710 oldOp.getParametersAttr());
711 newOp.getBody().emplaceBlock();
712 state.recordModuleMapping(oldOp, newOp);
713 opsToProcess.push_back(newOp);
716 .Case<SimulationOp>([&](
auto oldOp) {
717 auto loc = oldOp.getLoc();
718 auto builder = OpBuilder::atBlockEnd(topLevelModule);
719 auto newOp = verif::SimulationOp::create(
720 builder, loc, oldOp.getNameAttr(), oldOp.getParametersAttr());
721 auto &body = newOp.getRegion().emplaceBlock();
722 body.addArgument(seq::ClockType::get(builder.getContext()), loc);
723 body.addArgument(builder.getI1Type(), loc);
724 state.recordModuleMapping(oldOp, newOp);
725 opsToProcess.push_back(newOp);
728 .Case<emit::FileOp>([&](
auto fileOp) {
729 fileOp->moveBefore(topLevelModule, topLevelModule->end());
730 opsToProcess.push_back(fileOp);
733 .Default([&](Operation *op) {
738 op->moveBefore(topLevelModule, topLevelModule->end());
744 return signalPassFailure();
747 state.typeAliases.freeze();
752 SmallVector<Attribute> dutHierarchyFiles;
753 SmallVector<Attribute> testHarnessHierarchyFiles;
754 circuitAnno.removeAnnotations([&](
Annotation annotation) {
756 auto file = hw::OutputFileAttr::getFromFilename(
758 annotation.
getMember<StringAttr>(
"filename").getValue(),
760 dutHierarchyFiles.push_back(file);
764 auto file = hw::OutputFileAttr::getFromFilename(
766 annotation.
getMember<StringAttr>(
"filename").getValue(),
770 if (state.getTestHarness())
771 testHarnessHierarchyFiles.push_back(file);
773 dutHierarchyFiles.push_back(file);
779 if (!dutHierarchyFiles.empty())
780 state.getNewModule(state.getDut())
782 ArrayAttr::get(&getContext(), dutHierarchyFiles));
783 if (!testHarnessHierarchyFiles.empty())
784 state.getNewModule(state.getTestHarness())
786 ArrayAttr::get(&getContext(), testHarnessHierarchyFiles));
790 mlir::failableParallelForEach(&getContext(), opsToProcess, [&](
auto op) {
794 return signalPassFailure();
797 for (
auto bind : state.binds) {
802 for (
auto &[module, fragments] : state.fragments)
804 ArrayAttr::
get(&getContext(), fragments.getArrayRef()));
807 for (
auto oldNew : state.oldToNewModuleMap)
808 oldNew.first->erase();
810 if (!state.macroDeclNames.empty()) {
811 ImplicitLocOpBuilder b(UnknownLoc::get(&getContext()), circuit);
812 for (
auto name : state.macroDeclNames) {
813 sv::MacroDeclOp::create(b, name);
818 lowerFileHeader(circuit, state);
825void FIRRTLModuleLowering::lowerFileHeader(CircuitOp op,
826 CircuitLoweringState &state) {
829 ImplicitLocOpBuilder b(UnknownLoc::get(&getContext()), op);
833 auto emitGuardedDefine = [&](StringRef guard, StringRef defName,
834 StringRef defineTrue =
"",
835 StringRef defineFalse = StringRef()) {
836 if (!defineFalse.data()) {
837 assert(defineTrue.data() &&
"didn't define anything");
839 b, guard, [&]() { sv::MacroDefOp::create(b, defName, defineTrue); });
844 if (defineTrue.data())
845 sv::MacroDefOp::create(b, defName, defineTrue);
847 [&]() { sv::MacroDefOp::create(b, defName, defineFalse); });
852 auto emitGuard = [&](
const char *guard, llvm::function_ref<void(
void)> body) {
854 b, guard, [] {}, body);
857 if (state.usedFileDescriptorLib) {
859 SmallVector<hw::ModulePort> ports;
863 namePort.
name = b.getStringAttr(
"name");
864 namePort.
type = hw::StringType::get(b.getContext());
865 namePort.
dir = hw::ModulePort::Direction::Input;
866 ports.push_back(namePort);
870 fdPort.
name = b.getStringAttr(
"fd");
871 fdPort.
type = b.getIntegerType(32);
872 fdPort.
dir = hw::ModulePort::Direction::Output;
873 ports.push_back(fdPort);
876 auto moduleType = hw::ModuleType::get(b.getContext(), ports);
878 SmallVector<NamedAttribute> perArgumentsAttr;
879 perArgumentsAttr.push_back(
880 {sv::FuncOp::getExplicitlyReturnedAttrName(), b.getUnitAttr()});
882 SmallVector<Attribute> argumentAttr = {
883 DictionaryAttr::get(b.getContext(), {}),
884 DictionaryAttr::get(b.getContext(), perArgumentsAttr)};
887 auto func = sv::FuncOp::create(
889 "__circt_lib_logging::FileDescriptor::get", moduleType,
892 {b.getDictionaryAttr({}), b.getDictionaryAttr(perArgumentsAttr)}),
898 b.getStringAttr(
"__circt_lib_logging::FileDescriptor::get"));
901 sv::MacroDeclOp::create(b,
"__CIRCT_LIB_LOGGING");
903 emit::FragmentOp::create(b,
"CIRCT_LIB_LOGGING_FRAGMENT", [&] {
904 emitGuard(
"SYNTHESIS", [&]() {
905 emitGuard(
"__CIRCT_LIB_LOGGING", [&]() {
906 sv::VerbatimOp::create(b, R
"(// CIRCT Logging Library
907package __circt_lib_logging;
908 class FileDescriptor;
909 static int global_id [string];
910 static function int get(string name);
911 if (global_id.exists(name) == 32'h0) begin
912 global_id[name] = $fopen(name);
913 if (global_id[name] == 32'h0)
914 $error("Failed to open file %s", name);
916 return global_id[name];
922 sv::MacroDefOp::create(b, "__CIRCT_LIB_LOGGING",
"");
928 if (state.usedPrintf) {
929 sv::MacroDeclOp::create(b,
"PRINTF_COND");
930 sv::MacroDeclOp::create(b,
"PRINTF_COND_");
931 emit::FragmentOp::create(b,
"PRINTF_COND_FRAGMENT", [&] {
932 sv::VerbatimOp::create(
933 b,
"\n// Users can define 'PRINTF_COND' to add an extra gate to "
935 emitGuard(
"PRINTF_COND_", [&]() {
936 emitGuardedDefine(
"PRINTF_COND",
"PRINTF_COND_",
"(`PRINTF_COND)",
"1");
941 if (state.usedAssertVerboseCond) {
942 sv::MacroDeclOp::create(b,
"ASSERT_VERBOSE_COND");
943 sv::MacroDeclOp::create(b,
"ASSERT_VERBOSE_COND_");
944 emit::FragmentOp::create(b,
"ASSERT_VERBOSE_COND_FRAGMENT", [&] {
945 sv::VerbatimOp::create(
946 b,
"\n// Users can define 'ASSERT_VERBOSE_COND' to add an extra "
947 "gate to assert error printing.");
948 emitGuard(
"ASSERT_VERBOSE_COND_", [&]() {
949 emitGuardedDefine(
"ASSERT_VERBOSE_COND",
"ASSERT_VERBOSE_COND_",
950 "(`ASSERT_VERBOSE_COND)",
"1");
955 if (state.usedStopCond) {
956 sv::MacroDeclOp::create(b,
"STOP_COND");
957 sv::MacroDeclOp::create(b,
"STOP_COND_");
958 emit::FragmentOp::create(b,
"STOP_COND_FRAGMENT", [&] {
959 sv::VerbatimOp::create(
960 b,
"\n// Users can define 'STOP_COND' to add an extra gate "
961 "to stop conditions.");
962 emitGuard(
"STOP_COND_", [&]() {
963 emitGuardedDefine(
"STOP_COND",
"STOP_COND_",
"(`STOP_COND)",
"1");
970FIRRTLModuleLowering::lowerPorts(ArrayRef<PortInfo> firrtlPorts,
971 SmallVectorImpl<hw::PortInfo> &ports,
972 Operation *moduleOp, StringRef moduleName,
974 ports.reserve(firrtlPorts.size());
976 size_t numResults = 0;
977 for (
auto e :
llvm::enumerate(firrtlPorts)) {
979 size_t portNo = e.index();
984 if (firrtlPort.
sym.size() > 1 ||
985 (firrtlPort.
sym.size() == 1 && !firrtlPort.
sym.getSymName()))
986 return emitError(firrtlPort.
loc)
987 <<
"cannot lower aggregate port " << firrtlPort.
name
988 <<
" with field sensitive symbols, HW dialect does not support "
989 "per field symbols yet.";
990 hwPort.
setSym(firrtlPort.
sym, moduleOp->getContext());
992 if (hadDontTouch && !hwPort.
getSym()) {
993 if (hwPort.
type.isInteger(0)) {
994 if (enableAnnotationWarning) {
995 mlir::emitWarning(firrtlPort.
loc)
996 <<
"zero width port " << hwPort.
name
997 <<
" has dontTouch annotation, removing anyway";
1003 hw::InnerSymAttr::get(StringAttr::get(
1004 moduleOp->getContext(),
1005 Twine(
"__") + moduleName + Twine(
"__DONTTOUCH__") +
1006 Twine(portNo) + Twine(
"__") + firrtlPort.
name.strref())),
1007 moduleOp->getContext());
1012 moduleOp->emitError(
"cannot lower this port type to HW");
1018 if (hwPort.
type.isInteger(0)) {
1019 auto sym = hwPort.
getSym();
1020 if (sym && !sym.empty()) {
1021 return mlir::emitError(firrtlPort.
loc)
1022 <<
"zero width port " << hwPort.
name
1023 <<
" is referenced by name [" << sym
1024 <<
"] (e.g. in an XMR) but must be removed";
1031 hwPort.
dir = hw::ModulePort::Direction::Output;
1032 hwPort.
argNum = numResults++;
1033 }
else if (firrtlPort.
isInput()) {
1034 hwPort.
dir = hw::ModulePort::Direction::Input;
1035 hwPort.
argNum = numArgs++;
1039 hwPort.
type = hw::InOutType::get(hwPort.
type);
1040 hwPort.
dir = hw::ModulePort::Direction::InOut;
1041 hwPort.
argNum = numArgs++;
1043 hwPort.
loc = firrtlPort.
loc;
1044 ports.push_back(hwPort);
1054 auto params = llvm::map_range(module.getParameters(), [](Attribute a) {
1055 return cast<ParamDeclAttr>(a);
1060 Builder builder(module);
1065 SmallVector<Attribute> newParams;
1066 for (
const ParamDeclAttr &entry : params) {
1067 auto name = entry.getName();
1068 auto type = entry.getType();
1069 auto value = ignoreValues ? Attribute() : entry.getValue();
1071 hw::ParamDeclAttr::get(builder.getContext(), name, type, value);
1072 newParams.push_back(paramAttr);
1074 return builder.getArrayAttr(newParams);
1077bool FIRRTLModuleLowering::handleForceNameAnnos(
1080 bool failed =
false;
1086 auto sym = anno.
getMember<FlatSymbolRefAttr>(
"circt.nonlocal");
1093 auto diag = oldModule.emitOpError()
1095 <<
"' that is not a non-local annotation";
1096 diag.attachNote() <<
"the erroneous annotation is '" << anno.
getDict()
1107 auto diag = oldModule.emitOpError()
1109 <<
"' whose non-local symbol, '" << sym
1110 <<
"' does not exist in the circuit";
1111 diag.attachNote() <<
"the erroneous annotation is '" << anno.
getDict();
1124 cast<hw::InnerRefAttr>(nla.getNamepath().getValue().take_back(2)[0]);
1126 {{inst.getModule(), inst.getName()}, anno.
getMember(
"name")});
1127 if (!inserted.second &&
1128 (anno.
getMember(
"name") != (inserted.first->second))) {
1129 auto diag = oldModule.emitError()
1131 <<
"' with different names: " << inserted.first->second
1132 <<
" was not " << anno.
getMember(
"name");
1133 diag.attachNote() <<
"the erroneous annotation is '" << anno.
getDict()
1144FIRRTLModuleLowering::lowerExtModule(FExtModuleOp oldModule,
1145 Block *topLevelModule,
1148 SmallVector<PortInfo> firrtlPorts = oldModule.getPorts();
1149 SmallVector<hw::PortInfo, 8> ports;
1150 if (failed(lowerPorts(firrtlPorts, ports, oldModule, oldModule.getName(),
1154 StringRef verilogName;
1155 if (
auto defName = oldModule.getDefname())
1156 verilogName = defName.value();
1159 auto builder = OpBuilder::atBlockEnd(topLevelModule);
1160 auto nameAttr = builder.getStringAttr(oldModule.getName());
1165 auto newModule = hw::HWModuleExternOp::create(
1166 builder, oldModule.getLoc(), nameAttr, ports, verilogName, parameters);
1167 SymbolTable::setSymbolVisibility(newModule,
1168 SymbolTable::getSymbolVisibility(oldModule));
1170 bool hasOutputPort =
1171 llvm::any_of(firrtlPorts, [&](
auto p) {
return p.isOutput(); });
1172 if (!hasOutputPort &&
1175 newModule->setAttr(
"firrtl.extract.cover.extra", builder.getUnitAttr());
1181 loweringState.processRemainingAnnotations(oldModule, annos);
1186FIRRTLModuleLowering::lowerMemModule(FMemModuleOp oldModule,
1187 Block *topLevelModule,
1190 SmallVector<PortInfo> firrtlPorts = oldModule.getPorts();
1191 SmallVector<hw::PortInfo, 8> ports;
1192 if (failed(lowerPorts(firrtlPorts, ports, oldModule, oldModule.getName(),
1197 auto builder = OpBuilder::atBlockEnd(topLevelModule);
1198 auto newModule = hw::HWModuleExternOp::create(
1199 builder, oldModule.getLoc(), oldModule.getModuleNameAttr(), ports,
1200 oldModule.getModuleNameAttr());
1208FIRRTLModuleLowering::lowerModule(FModuleOp oldModule, Block *topLevelModule,
1211 SmallVector<PortInfo> firrtlPorts = oldModule.getPorts();
1212 SmallVector<hw::PortInfo, 8> ports;
1213 if (failed(lowerPorts(firrtlPorts, ports, oldModule, oldModule.getName(),
1218 auto builder = OpBuilder::atBlockEnd(topLevelModule);
1219 auto nameAttr = builder.getStringAttr(oldModule.getName());
1221 hw::HWModuleOp::create(builder, oldModule.getLoc(), nameAttr, ports);
1223 if (
auto comment = oldModule->getAttrOfType<StringAttr>(
"comment"))
1224 newModule.setCommentAttr(comment);
1227 SmallVector<StringRef, 13> attrNames = {
1228 "annotations",
"convention",
"layers",
1229 "portNames",
"sym_name",
"portDirections",
1230 "portTypes",
"portAnnotations",
"portSymbols",
1231 "portLocations",
"parameters", SymbolTable::getVisibilityAttrName(),
1234 DenseSet<StringRef> attrSet(attrNames.begin(), attrNames.end());
1235 SmallVector<NamedAttribute> newAttrs(newModule->getAttrs());
1237 llvm::make_filter_range(oldModule->getAttrs(), [&](auto namedAttr) {
1238 return !attrSet.count(namedAttr.getName()) &&
1239 !newModule->getAttrDictionary().contains(namedAttr.getName());
1241 newAttrs.push_back(i);
1243 newModule->setAttrs(newAttrs);
1247 SymbolTable::setSymbolVisibility(newModule,
1248 SymbolTable::getSymbolVisibility(oldModule));
1254 newModule->setAttr(
"firrtl.extract.cover.extra", builder.getUnitAttr());
1258 if (
auto testBenchDir =
loweringState.getTestBenchDirectory())
1260 if (!newModule->hasAttr(
"output_file"))
1261 newModule->setAttr(
"output_file", testBenchDir);
1262 newModule->setAttr(
"firrtl.extract.do_not_extract",
1263 builder.getUnitAttr());
1264 newModule.setCommentAttr(
1265 builder.getStringAttr(
"VCS coverage exclude_file"));
1271 loweringState.processRemainingAnnotations(oldModule, annos);
1280 Operation *insertPoint) {
1281 if (!value.hasOneUse())
1284 auto attach = dyn_cast<AttachOp>(*value.user_begin());
1285 if (!attach || attach.getNumOperands() != 2)
1289 auto loweredType =
lowerType(value.getType());
1290 if (loweredType.isInteger(0))
1295 auto attachedValue = attach.getOperand(attach.getOperand(0) == value);
1296 auto *op = attachedValue.getDefiningOp();
1297 if (op && op->getBlock() == insertPoint->getBlock() &&
1298 !op->isBeforeInBlock(insertPoint))
1303 ImplicitLocOpBuilder builder(insertPoint->getLoc(), insertPoint);
1319 if (type_isa<AnalogType>(flipValue.getType()))
1322 Operation *connectOp =
nullptr;
1323 for (
auto &use : flipValue.getUses()) {
1326 if (use.getOperandNumber() != 0)
1328 if (!isa<ConnectOp, MatchingConnectOp>(use.getOwner()))
1334 connectOp = use.getOwner();
1344 loweringState.lowerType(flipValue.getType(), flipValue.getLoc());
1345 if (loweredType.isInteger(0))
1350 ImplicitLocOpBuilder builder(insertPoint->getLoc(), insertPoint);
1352 auto connectSrc = connectOp->getOperand(1);
1355 if (!isa<FIRRTLType>(connectSrc.getType())) {
1361 if (!type_cast<FIRRTLBaseType>(connectSrc.getType()).isPassive())
1363 mlir::UnrealizedConversionCastOp::create(
1365 type_cast<FIRRTLBaseType>(connectSrc.getType()).getPassiveType(),
1371 auto destTy = type_cast<FIRRTLBaseType>(flipValue.getType()).getPassiveType();
1373 if (destTy != connectSrc.getType() &&
1374 (isa<BaseTypeAliasType>(connectSrc.getType()) ||
1375 isa<BaseTypeAliasType>(destTy))) {
1377 builder.createOrFold<BitCastOp>(flipValue.getType(), connectSrc);
1379 if (!destTy.isGround()) {
1381 if (destTy != type_cast<FIRRTLType>(connectSrc.getType()))
1383 }
else if (destTy.getBitWidthOrSentinel() !=
1384 type_cast<FIRRTLBaseType>(connectSrc.getType())
1385 .getBitWidthOrSentinel()) {
1388 auto destWidth = destTy.getBitWidthOrSentinel();
1389 assert(destWidth != -1 &&
"must know integer widths");
1390 connectSrc = builder.createOrFold<PadPrimOp>(destTy, connectSrc, destWidth);
1402 SmallVector<SubfieldOp> accesses;
1403 for (
auto *op : structValue.getUsers()) {
1404 assert(isa<SubfieldOp>(op));
1405 auto fieldAccess = cast<SubfieldOp>(op);
1407 fieldAccess.getInput().getType().base().getElementIndex(field);
1408 if (elemIndex && *elemIndex == fieldAccess.getFieldIndex())
1409 accesses.push_back(fieldAccess);
1417LogicalResult FIRRTLModuleLowering::lowerModulePortsAndMoveBody(
1420 ImplicitLocOpBuilder bodyBuilder(oldModule.getLoc(), newModule.getBody());
1427 bodyBuilder.setInsertionPoint(cursor);
1430 SmallVector<PortInfo> firrtlPorts = oldModule.getPorts();
1431 assert(oldModule.getBody().getNumArguments() == firrtlPorts.size() &&
1432 "port count mismatch");
1434 SmallVector<Value, 4> outputs;
1437 auto *outputOp = newModule.getBodyBlock()->getTerminator();
1438 ImplicitLocOpBuilder outputBuilder(oldModule.getLoc(), outputOp);
1440 unsigned nextHWInputArg = 0;
1441 int hwPortIndex = -1;
1442 for (
auto [firrtlPortIndex, port] :
llvm::enumerate(firrtlPorts)) {
1444 auto oldArg = oldModule.getBody().getArgument(firrtlPortIndex);
1447 type_isa<FIRRTLBaseType>(port.type) &&
1448 type_cast<FIRRTLBaseType>(port.type).getBitWidthOrSentinel() == 0;
1452 if (!port.isOutput() && !isZeroWidth) {
1455 Value newArg = newModule.getBody().getArgument(nextHWInputArg++);
1461 oldArg.replaceAllUsesWith(newArg);
1467 if (isZeroWidth && port.isInput()) {
1469 WireOp::create(bodyBuilder, port.type,
1470 "." + port.getName().str() +
".0width_input")
1472 oldArg.replaceAllUsesWith(newArg);
1480 outputs.push_back(value);
1481 assert(oldArg.use_empty() &&
"should have removed all uses of oldArg");
1487 auto newArg = WireOp::create(bodyBuilder, port.type,
1488 "." + port.getName().str() +
".output");
1491 oldArg.replaceAllUsesWith(newArg.getResult());
1494 auto resultHWType =
loweringState.lowerType(port.type, port.loc);
1495 if (!resultHWType.isInteger(0)) {
1498 outputs.push_back(output);
1501 if (
auto sym = newModule.getPort(hwPortIndex).getSym()) {
1502 newArg.setInnerSymAttr(sym);
1503 newModule.setPortSymbolAttr(hwPortIndex, {});
1509 outputOp->setOperands(outputs);
1512 auto &oldBlockInstList = oldModule.getBodyBlock()->getOperations();
1513 auto &newBlockInstList = newModule.getBodyBlock()->getOperations();
1514 newBlockInstList.splice(Block::iterator(cursor), oldBlockInstList,
1515 oldBlockInstList.begin(), oldBlockInstList.end());
1526FIRRTLModuleLowering::lowerFormalBody(verif::FormalOp newOp,
1528 auto builder = OpBuilder::atBlockEnd(&newOp.getBody().front());
1533 auto oldOp = cast<FormalOp>(
loweringState.getOldModule(newOp));
1534 auto moduleName = oldOp.getModuleNameAttr().getAttr();
1535 auto oldModule = cast<FModuleOp>(
1536 loweringState.getInstanceGraph().lookup(moduleName)->getModule());
1537 auto newModule = cast<hw::HWModuleOp>(
loweringState.getNewModule(oldModule));
1540 SmallVector<Value> symbolicInputs;
1541 for (
auto arg : newModule.getBody().getArguments())
1542 symbolicInputs.push_back(
1543 verif::SymbolicValueOp::create(builder, arg.
getLoc(), arg.getType()));
1546 hw::InstanceOp::create(builder, newOp.getLoc(), newModule,
1547 newModule.getNameAttr(), symbolicInputs);
1554FIRRTLModuleLowering::lowerSimulationBody(verif::SimulationOp newOp,
1556 auto builder = OpBuilder::atBlockEnd(newOp.getBody());
1559 auto oldOp = cast<SimulationOp>(
loweringState.getOldModule(newOp));
1560 auto moduleName = oldOp.getModuleNameAttr().getAttr();
1561 auto oldModule = cast<FModuleLike>(
1562 *
loweringState.getInstanceGraph().lookup(moduleName)->getModule());
1564 cast<hw::HWModuleLike>(
loweringState.getNewModule(oldModule));
1568 SmallVector<Value> inputs(newOp.getBody()->args_begin(),
1569 newOp.getBody()->args_end());
1570 auto instOp = hw::InstanceOp::create(builder, newOp.getLoc(), newModule,
1571 newModule.getNameAttr(), inputs);
1572 verif::YieldOp::create(builder, newOp.getLoc(), instOp.getResults());
1582struct FIRRTLLowering :
public FIRRTLVisitor<FIRRTLLowering, LogicalResult> {
1584 FIRRTLLowering(
hw::HWModuleOp module, CircuitLoweringState &circuitState)
1585 : theModule(module), circuitState(circuitState),
1586 builder(module.
getLoc(), module.getContext()), moduleNamespace(module),
1587 backedgeBuilder(builder, module.
getLoc()) {}
1589 LogicalResult
run();
1592 Value getOrCreateClockConstant(seq::ClockConst clock);
1593 Value getOrCreateIntConstant(
const APInt &value);
1594 Value getOrCreateIntConstant(
unsigned numBits, uint64_t val,
1595 bool isSigned =
false) {
1596 return getOrCreateIntConstant(APInt(numBits, val, isSigned));
1598 Attribute getOrCreateAggregateConstantAttribute(Attribute value, Type type);
1599 Value getOrCreateXConstant(
unsigned numBits);
1600 Value getOrCreateZConstant(Type type);
1601 Value getPossiblyInoutLoweredValue(Value value);
1602 Value getLoweredValue(Value value);
1603 Value getLoweredNonClockValue(Value value);
1604 Value getLoweredAndExtendedValue(Value value, Type destType);
1605 Value getLoweredAndExtOrTruncValue(Value value, Type destType);
1606 LogicalResult setLowering(Value orig, Value result);
1607 LogicalResult setPossiblyFoldedLowering(Value orig, Value result);
1608 template <
typename ResultOpType,
typename... CtorArgTypes>
1609 LogicalResult setLoweringTo(Operation *orig, CtorArgTypes... args);
1610 template <
typename ResultOpType,
typename... CtorArgTypes>
1611 LogicalResult setLoweringToLTL(Operation *orig, CtorArgTypes... args);
1612 Backedge createBackedge(Location loc, Type type);
1613 Backedge createBackedge(Value orig, Type type);
1614 bool updateIfBackedge(Value dest, Value src);
1617 bool requiresInnerSymbol(hw::InnerSymbolOpInterface op) {
1622 if (
auto forceable = dyn_cast<Forceable>(op.getOperation()))
1623 if (forceable.isForceable())
1631 hw::InnerSymAttr lowerInnerSymbol(hw::InnerSymbolOpInterface op) {
1632 auto attr = op.getInnerSymAttr();
1636 if (requiresInnerSymbol(op))
1638 op.getContext(), attr, 0,
1643 void runWithInsertionPointAtEndOfBlock(
const std::function<
void(
void)> &fn,
1647 Value getReadValue(Value v);
1649 Value getNonClockValue(Value v);
1651 void addToAlwaysBlock(sv::EventControl clockEdge, Value clock,
1652 sv::ResetType resetStyle, sv::EventControl resetEdge,
1653 Value reset,
const std::function<
void(
void)> &body = {},
1654 const std::function<void(
void)> &resetBody = {});
1655 void addToAlwaysBlock(Value clock,
1656 const std::function<
void(
void)> &body = {}) {
1657 addToAlwaysBlock(sv::EventControl::AtPosEdge, clock, sv::ResetType(),
1658 sv::EventControl(), Value(), body,
1659 std::function<
void(
void)>());
1662 LogicalResult emitGuards(Location loc, ArrayRef<Attribute> guards,
1663 std::function<
void(
void)>
emit);
1664 void addToIfDefBlock(StringRef cond, std::function<
void(
void)> thenCtor,
1665 std::function<
void(
void)> elseCtor = {});
1666 void addToInitialBlock(std::function<
void(
void)> body);
1667 void addIfProceduralBlock(Value cond, std::function<
void(
void)> thenCtor,
1668 std::function<
void(
void)> elseCtor = {});
1669 Value getExtOrTruncAggregateValue(Value array,
FIRRTLBaseType sourceType,
1671 bool allowTruncate);
1672 Value createArrayIndexing(Value array, Value index);
1673 Value createValueWithMuxAnnotation(Operation *op,
bool isMux2);
1675 using FIRRTLVisitor<FIRRTLLowering, LogicalResult>::visitExpr;
1676 using FIRRTLVisitor<FIRRTLLowering, LogicalResult>::visitDecl;
1677 using FIRRTLVisitor<FIRRTLLowering, LogicalResult>::visitStmt;
1680 enum UnloweredOpResult { AlreadyLowered, NowLowered, LoweringFailure };
1681 UnloweredOpResult handleUnloweredOp(Operation *op);
1682 LogicalResult visitExpr(ConstantOp op);
1683 LogicalResult visitExpr(SpecialConstantOp op);
1684 LogicalResult visitExpr(SubindexOp op);
1685 LogicalResult visitExpr(SubaccessOp op);
1686 LogicalResult visitExpr(SubfieldOp op);
1687 LogicalResult visitExpr(VectorCreateOp op);
1688 LogicalResult visitExpr(BundleCreateOp op);
1689 LogicalResult visitExpr(FEnumCreateOp op);
1690 LogicalResult visitExpr(AggregateConstantOp op);
1691 LogicalResult visitExpr(IsTagOp op);
1692 LogicalResult visitExpr(SubtagOp op);
1693 LogicalResult visitExpr(TagExtractOp op);
1696 if (
auto castOp = dyn_cast<mlir::UnrealizedConversionCastOp>(op))
1697 return visitUnrealizedConversionCast(castOp);
1702 LogicalResult visitDecl(WireOp op);
1703 LogicalResult visitDecl(NodeOp op);
1704 LogicalResult visitDecl(RegOp op);
1705 LogicalResult visitDecl(RegResetOp op);
1706 LogicalResult visitDecl(MemOp op);
1707 LogicalResult visitDecl(InstanceOp oldInstance);
1708 LogicalResult visitDecl(VerbatimWireOp op);
1709 LogicalResult visitDecl(ContractOp op);
1712 LogicalResult lowerNoopCast(Operation *op);
1713 LogicalResult visitExpr(AsSIntPrimOp op);
1714 LogicalResult visitExpr(AsUIntPrimOp op);
1715 LogicalResult visitExpr(AsClockPrimOp op);
1716 LogicalResult visitExpr(AsAsyncResetPrimOp op) {
return lowerNoopCast(op); }
1718 LogicalResult visitExpr(HWStructCastOp op);
1719 LogicalResult visitExpr(BitCastOp op);
1721 visitUnrealizedConversionCast(mlir::UnrealizedConversionCastOp op);
1722 LogicalResult visitExpr(CvtPrimOp op);
1723 LogicalResult visitExpr(NotPrimOp op);
1724 LogicalResult visitExpr(NegPrimOp op);
1725 LogicalResult visitExpr(PadPrimOp op);
1726 LogicalResult visitExpr(XorRPrimOp op);
1727 LogicalResult visitExpr(AndRPrimOp op);
1728 LogicalResult visitExpr(OrRPrimOp op);
1731 template <
typename ResultUnsignedOpType,
1732 typename ResultSignedOpType = ResultUnsignedOpType>
1733 LogicalResult lowerBinOp(Operation *op);
1734 template <
typename ResultOpType>
1735 LogicalResult lowerBinOpToVariadic(Operation *op);
1737 template <
typename ResultOpType>
1738 LogicalResult lowerElementwiseLogicalOp(Operation *op);
1740 LogicalResult lowerCmpOp(Operation *op, ICmpPredicate signedOp,
1741 ICmpPredicate unsignedOp);
1742 template <
typename SignedOp,
typename Un
signedOp>
1743 LogicalResult lowerDivLikeOp(Operation *op);
1745 LogicalResult visitExpr(CatPrimOp op);
1747 LogicalResult visitExpr(AndPrimOp op) {
1748 return lowerBinOpToVariadic<comb::AndOp>(op);
1750 LogicalResult visitExpr(OrPrimOp op) {
1751 return lowerBinOpToVariadic<comb::OrOp>(op);
1753 LogicalResult visitExpr(XorPrimOp op) {
1754 return lowerBinOpToVariadic<comb::XorOp>(op);
1756 LogicalResult visitExpr(ElementwiseOrPrimOp op) {
1757 return lowerElementwiseLogicalOp<comb::OrOp>(op);
1759 LogicalResult visitExpr(ElementwiseAndPrimOp op) {
1760 return lowerElementwiseLogicalOp<comb::AndOp>(op);
1762 LogicalResult visitExpr(ElementwiseXorPrimOp op) {
1763 return lowerElementwiseLogicalOp<comb::XorOp>(op);
1765 LogicalResult visitExpr(AddPrimOp op) {
1766 return lowerBinOpToVariadic<comb::AddOp>(op);
1768 LogicalResult visitExpr(EQPrimOp op) {
1769 return lowerCmpOp(op, ICmpPredicate::eq, ICmpPredicate::eq);
1771 LogicalResult visitExpr(NEQPrimOp op) {
1772 return lowerCmpOp(op, ICmpPredicate::ne, ICmpPredicate::ne);
1774 LogicalResult visitExpr(LTPrimOp op) {
1775 return lowerCmpOp(op, ICmpPredicate::slt, ICmpPredicate::ult);
1777 LogicalResult visitExpr(LEQPrimOp op) {
1778 return lowerCmpOp(op, ICmpPredicate::sle, ICmpPredicate::ule);
1780 LogicalResult visitExpr(GTPrimOp op) {
1781 return lowerCmpOp(op, ICmpPredicate::sgt, ICmpPredicate::ugt);
1783 LogicalResult visitExpr(GEQPrimOp op) {
1784 return lowerCmpOp(op, ICmpPredicate::sge, ICmpPredicate::uge);
1787 LogicalResult visitExpr(SubPrimOp op) {
return lowerBinOp<comb::SubOp>(op); }
1788 LogicalResult visitExpr(MulPrimOp op) {
1789 return lowerBinOpToVariadic<comb::MulOp>(op);
1791 LogicalResult visitExpr(DivPrimOp op) {
1792 return lowerDivLikeOp<comb::DivSOp, comb::DivUOp>(op);
1794 LogicalResult visitExpr(RemPrimOp op) {
1795 return lowerDivLikeOp<comb::ModSOp, comb::ModUOp>(op);
1799 LogicalResult visitExpr(IsXIntrinsicOp op);
1800 LogicalResult visitExpr(PlusArgsTestIntrinsicOp op);
1801 LogicalResult visitExpr(PlusArgsValueIntrinsicOp op);
1802 LogicalResult visitStmt(FPGAProbeIntrinsicOp op);
1803 LogicalResult visitExpr(ClockInverterIntrinsicOp op);
1804 LogicalResult visitExpr(ClockDividerIntrinsicOp op);
1805 LogicalResult visitExpr(SizeOfIntrinsicOp op);
1806 LogicalResult visitExpr(ClockGateIntrinsicOp op);
1807 LogicalResult visitExpr(LTLAndIntrinsicOp op);
1808 LogicalResult visitExpr(LTLOrIntrinsicOp op);
1809 LogicalResult visitExpr(LTLIntersectIntrinsicOp op);
1810 LogicalResult visitExpr(LTLDelayIntrinsicOp op);
1811 LogicalResult visitExpr(LTLConcatIntrinsicOp op);
1812 LogicalResult visitExpr(LTLRepeatIntrinsicOp op);
1813 LogicalResult visitExpr(LTLGoToRepeatIntrinsicOp op);
1814 LogicalResult visitExpr(LTLNonConsecutiveRepeatIntrinsicOp op);
1815 LogicalResult visitExpr(LTLNotIntrinsicOp op);
1816 LogicalResult visitExpr(LTLImplicationIntrinsicOp op);
1817 LogicalResult visitExpr(LTLUntilIntrinsicOp op);
1818 LogicalResult visitExpr(LTLEventuallyIntrinsicOp op);
1819 LogicalResult visitExpr(LTLClockIntrinsicOp op);
1821 template <
typename TargetOp,
typename IntrinsicOp>
1822 LogicalResult lowerVerifIntrinsicOp(IntrinsicOp op);
1823 LogicalResult visitStmt(VerifAssertIntrinsicOp op);
1824 LogicalResult visitStmt(VerifAssumeIntrinsicOp op);
1825 LogicalResult visitStmt(VerifCoverIntrinsicOp op);
1826 LogicalResult visitStmt(VerifRequireIntrinsicOp op);
1827 LogicalResult visitStmt(VerifEnsureIntrinsicOp op);
1828 LogicalResult visitExpr(HasBeenResetIntrinsicOp op);
1829 LogicalResult visitStmt(UnclockedAssumeIntrinsicOp op);
1832 LogicalResult visitExpr(BitsPrimOp op);
1833 LogicalResult visitExpr(InvalidValueOp op);
1834 LogicalResult visitExpr(HeadPrimOp op);
1835 LogicalResult visitExpr(ShlPrimOp op);
1836 LogicalResult visitExpr(ShrPrimOp op);
1837 LogicalResult visitExpr(DShlPrimOp op) {
1838 return lowerDivLikeOp<comb::ShlOp, comb::ShlOp>(op);
1840 LogicalResult visitExpr(DShrPrimOp op) {
1841 return lowerDivLikeOp<comb::ShrSOp, comb::ShrUOp>(op);
1843 LogicalResult visitExpr(DShlwPrimOp op) {
1844 return lowerDivLikeOp<comb::ShlOp, comb::ShlOp>(op);
1846 LogicalResult visitExpr(TailPrimOp op);
1847 LogicalResult visitExpr(MuxPrimOp op);
1848 LogicalResult visitExpr(Mux2CellIntrinsicOp op);
1849 LogicalResult visitExpr(Mux4CellIntrinsicOp op);
1850 LogicalResult visitExpr(MultibitMuxOp op);
1851 LogicalResult visitExpr(VerbatimExprOp op);
1852 LogicalResult visitExpr(XMRRefOp op);
1853 LogicalResult visitExpr(XMRDerefOp op);
1856 LogicalResult visitExpr(TimeOp op);
1857 LogicalResult visitExpr(HierarchicalModuleNameOp op);
1860 LogicalResult lowerVerificationStatement(
1861 Operation *op, StringRef labelPrefix, Value clock, Value predicate,
1862 Value enable, StringAttr messageAttr, ValueRange operands,
1863 StringAttr nameAttr,
bool isConcurrent, EventControl eventControl);
1865 LogicalResult visitStmt(SkipOp op);
1867 FailureOr<bool> lowerConnect(Value dest, Value srcVal);
1868 LogicalResult visitStmt(ConnectOp op);
1869 LogicalResult visitStmt(MatchingConnectOp op);
1870 LogicalResult visitStmt(ForceOp op);
1872 std::optional<Value> getLoweredFmtOperand(Value operand);
1873 LogicalResult loweredFmtOperands(ValueRange operands,
1874 SmallVectorImpl<Value> &loweredOperands);
1875 FailureOr<Value> callFileDescriptorLib(
const FileDescriptorInfo &info);
1879 LogicalResult lowerStatementWithFd(
1880 const FileDescriptorInfo &fileDescriptorInfo, Value clock, Value cond,
1881 const std::function<LogicalResult(Value)> &fn,
bool usePrintfCond);
1885 LogicalResult visitPrintfLike(T op,
1886 const FileDescriptorInfo &fileDescriptorInfo,
1887 bool usePrintfCond);
1888 LogicalResult visitStmt(PrintFOp op) {
return visitPrintfLike(op, {},
true); }
1889 LogicalResult visitStmt(FPrintFOp op);
1890 LogicalResult visitStmt(FFlushOp op);
1891 LogicalResult visitStmt(StopOp op);
1892 LogicalResult visitStmt(AssertOp op);
1893 LogicalResult visitStmt(AssumeOp op);
1894 LogicalResult visitStmt(CoverOp op);
1895 LogicalResult visitStmt(AttachOp op);
1896 LogicalResult visitStmt(RefForceOp op);
1897 LogicalResult visitStmt(RefForceInitialOp op);
1898 LogicalResult visitStmt(RefReleaseOp op);
1899 LogicalResult visitStmt(RefReleaseInitialOp op);
1900 LogicalResult visitStmt(BindOp op);
1902 FailureOr<Value> lowerSubindex(SubindexOp op, Value input);
1903 FailureOr<Value> lowerSubaccess(SubaccessOp op, Value input);
1904 FailureOr<Value> lowerSubfield(SubfieldOp op, Value input);
1906 LogicalResult fixupLTLOps();
1909 return circuitState.lowerType(type, builder.getLoc());
1917 CircuitLoweringState &circuitState;
1920 ImplicitLocOpBuilder builder;
1925 DenseMap<Value, Value> valueMapping;
1929 DenseMap<Value, Value> fromClockMapping;
1933 DenseMap<Attribute, Value> hwConstantMap;
1934 DenseMap<std::pair<Attribute, Type>, Attribute> hwAggregateConstantMap;
1938 DenseMap<unsigned, Value> hwConstantXMap;
1939 DenseMap<Type, Value> hwConstantZMap;
1945 DenseMap<Value, Value> readInOutCreated;
1948 DenseMap<StringAttr, sv::RegOp> fileNameToFileDescriptor;
1952 using AlwaysKeyType = std::tuple<
Block *, sv::EventControl, Value,
1953 sv::ResetType, sv::EventControl, Value>;
1970 llvm::MapVector<Value, Value> backedges;
1977 DenseSet<Operation *> maybeUnusedValues;
1979 void maybeUnused(Operation *op) { maybeUnusedValues.insert(op); }
1980 void maybeUnused(Value value) {
1981 if (
auto *op = value.getDefiningOp())
1993 SetVector<Operation *> ltlOpFixupWorklist;
1998 SmallVector<std::pair<Block::iterator, Block::iterator>> worklist;
2000 void addToWorklist(Block &block) {
2001 worklist.push_back({block.begin(), block.end()});
2003 void addToWorklist(Region ®ion) {
2004 for (
auto &block :
llvm::reverse(region))
2005 addToWorklist(block);
2016LogicalResult FIRRTLModuleLowering::lowerFileBody(emit::FileOp fileOp) {
2017 OpBuilder b(&getContext());
2018 fileOp->walk([&](Operation *op) {
2019 if (
auto bindOp = dyn_cast<BindOp>(op)) {
2020 b.setInsertionPointAfter(bindOp);
2021 sv::BindOp::create(b, bindOp.getLoc(), bindOp.getInstanceAttr());
2029FIRRTLModuleLowering::lowerBody(Operation *op,
2031 if (
auto moduleOp = dyn_cast<hw::HWModuleOp>(op))
2033 if (
auto formalOp = dyn_cast<verif::FormalOp>(op))
2035 if (
auto simulationOp = dyn_cast<verif::SimulationOp>(op))
2037 if (
auto fileOp = dyn_cast<emit::FileOp>(op))
2038 return lowerFileBody(fileOp);
2043LogicalResult FIRRTLLowering::run() {
2046 for (
auto arg : theModule.
getBodyBlock()->getArguments())
2047 if (failed(setLowering(arg, arg)))
2054 addToWorklist(theModule.getBody());
2055 SmallVector<Operation *, 16> opsToRemove;
2057 while (!worklist.empty()) {
2058 auto &[opsIt, opsEnd] = worklist.back();
2059 if (opsIt == opsEnd) {
2060 worklist.pop_back();
2063 Operation *op = &*opsIt++;
2065 builder.setInsertionPoint(op);
2066 builder.setLoc(op->getLoc());
2067 auto done = succeeded(dispatchVisitor(op));
2068 circuitState.processRemainingAnnotations(op,
AnnotationSet(op));
2070 opsToRemove.push_back(op);
2072 switch (handleUnloweredOp(op)) {
2073 case AlreadyLowered:
2076 opsToRemove.push_back(op);
2078 case LoweringFailure:
2090 for (
auto &[backedge, value] : backedges) {
2091 SmallVector<Location> driverLocs;
2097 if (backedge == value) {
2098 Location edgeLoc = backedge.getLoc();
2099 if (driverLocs.empty()) {
2100 mlir::emitError(edgeLoc,
"sink does not have a driver");
2102 auto diag = mlir::emitError(edgeLoc,
"sink in combinational loop");
2103 for (
auto loc : driverLocs)
2104 diag.attachNote(loc) <<
"through driver here";
2110 auto *it = backedges.find(value);
2111 if (it == backedges.end())
2114 driverLocs.push_back(value.getLoc());
2117 if (
auto *defOp = backedge.getDefiningOp())
2118 maybeUnusedValues.erase(defOp);
2119 backedge.replaceAllUsesWith(value);
2127 while (!opsToRemove.empty()) {
2128 auto *op = opsToRemove.pop_back_val();
2133 for (
auto result : op->getResults()) {
2137 auto builder = OpBuilder::atBlockBegin(theModule.getBodyBlock());
2139 builder.getIntegerType(0), 0);
2140 maybeUnusedValues.insert(zeroI0);
2142 result.replaceAllUsesWith(zeroI0);
2145 if (!op->use_empty()) {
2146 auto d = op->emitOpError(
2147 "still has uses; should remove ops in reverse order of visitation");
2148 SmallPtrSet<Operation *, 2> visited;
2149 for (
auto *user : op->getUsers())
2150 if (visited.insert(user).second)
2151 d.attachNote(user->
getLoc())
2152 <<
"used by " << user->
getName() <<
" op";
2155 maybeUnusedValues.erase(op);
2160 while (!maybeUnusedValues.empty()) {
2161 auto it = maybeUnusedValues.begin();
2163 maybeUnusedValues.erase(it);
2164 if (!isOpTriviallyDead(op))
2166 for (
auto operand : op->getOperands())
2167 if (auto *defOp = operand.getDefiningOp())
2168 maybeUnusedValues.insert(defOp);
2174 if (failed(fixupLTLOps()))
2185Value FIRRTLLowering::getOrCreateClockConstant(seq::ClockConst clock) {
2186 auto attr = seq::ClockConstAttr::get(theModule.getContext(), clock);
2188 auto &entry = hwConstantMap[attr];
2192 OpBuilder entryBuilder(&theModule.getBodyBlock()->front());
2193 entry = seq::ConstClockOp::create(entryBuilder, builder.getLoc(), attr);
2199Value FIRRTLLowering::getOrCreateIntConstant(
const APInt &value) {
2200 auto attr = builder.getIntegerAttr(
2201 builder.getIntegerType(value.getBitWidth()), value);
2203 auto &entry = hwConstantMap[attr];
2207 OpBuilder entryBuilder(&theModule.getBodyBlock()->front());
2214Attribute FIRRTLLowering::getOrCreateAggregateConstantAttribute(Attribute value,
2217 if (hw::type_isa<IntegerType>(type))
2218 return builder.getIntegerAttr(type, cast<IntegerAttr>(value).getValue());
2220 auto cache = hwAggregateConstantMap.lookup({value, type});
2225 SmallVector<Attribute> values;
2226 for (
auto e :
llvm::enumerate(cast<ArrayAttr>(value))) {
2228 if (
auto array = hw::type_dyn_cast<hw::ArrayType>(type))
2229 subType = array.getElementType();
2230 else if (
auto structType = hw::type_dyn_cast<hw::StructType>(type))
2231 subType = structType.getElements()[e.index()].type;
2233 assert(
false &&
"type must be either array or struct");
2235 values.push_back(getOrCreateAggregateConstantAttribute(e.value(), subType));
2239 if (hw::type_isa<hw::ArrayType>(type))
2240 std::reverse(values.begin(), values.end());
2242 auto &entry = hwAggregateConstantMap[{value, type}];
2243 entry = builder.getArrayAttr(values);
2251 const std::function<LogicalResult()> &fn) {
2252 assert(failedOperand &&
"Should be called on the failed operand");
2260Value FIRRTLLowering::getOrCreateXConstant(
unsigned numBits) {
2262 auto &entry = hwConstantXMap[numBits];
2266 OpBuilder entryBuilder(&theModule.getBodyBlock()->front());
2267 entry = sv::ConstantXOp::create(entryBuilder, builder.getLoc(),
2268 entryBuilder.getIntegerType(numBits));
2272Value FIRRTLLowering::getOrCreateZConstant(Type type) {
2273 auto &entry = hwConstantZMap[type];
2275 OpBuilder entryBuilder(&theModule.getBodyBlock()->front());
2276 entry = sv::ConstantZOp::create(entryBuilder, builder.getLoc(), type);
2285Value FIRRTLLowering::getPossiblyInoutLoweredValue(Value value) {
2287 if (
auto lowering = valueMapping.lookup(value)) {
2288 assert(!isa<FIRRTLType>(lowering.getType()) &&
2289 "Lowered value should be a non-FIRRTL value");
2298Value FIRRTLLowering::getLoweredValue(Value value) {
2299 auto result = getPossiblyInoutLoweredValue(value);
2305 if (isa<hw::InOutType>(result.getType()))
2306 return getReadValue(result);
2312Value FIRRTLLowering::getLoweredNonClockValue(Value value) {
2313 auto result = getLoweredValue(value);
2317 if (hw::type_isa<seq::ClockType>(result.getType()))
2318 return getNonClockValue(result);
2326Value FIRRTLLowering::getExtOrTruncAggregateValue(Value array,
2329 bool allowTruncate) {
2330 SmallVector<Value> resultBuffer;
2335 auto srcWidth = firrtl::type_cast<IntType>(sourceType).getWidthOrSentinel();
2336 auto destWidth = firrtl::type_cast<IntType>(destType).getWidthOrSentinel();
2337 auto resultType = builder.getIntegerType(destWidth);
2339 if (srcWidth == destWidth)
2342 if (srcWidth > destWidth) {
2346 builder.emitError(
"operand should not be a truncation");
2350 if (firrtl::type_cast<IntType>(sourceType).isSigned())
2351 return comb::createOrFoldSExt(value, resultType, builder);
2352 auto zero = getOrCreateIntConstant(destWidth - srcWidth, 0);
2360 return TypeSwitch<FIRRTLBaseType, LogicalResult>(srcType)
2361 .Case<FVectorType>([&](
auto srcVectorType) {
2362 auto destVectorType = firrtl::type_cast<FVectorType>(destType);
2363 unsigned size = resultBuffer.size();
2364 unsigned indexWidth =
2366 for (
size_t i = 0, e = std::min(srcVectorType.getNumElements(),
2367 destVectorType.getNumElements());
2369 auto iIdx = getOrCreateIntConstant(indexWidth, i);
2371 if (failed(recurse(arrayIndex, srcVectorType.getElementType(),
2372 destVectorType.getElementType())))
2375 SmallVector<Value> temp(resultBuffer.begin() + size,
2376 resultBuffer.end());
2378 resultBuffer.resize(size);
2379 resultBuffer.push_back(array);
2382 .Case<BundleType>([&](BundleType srcStructType) {
2383 auto destStructType = firrtl::type_cast<BundleType>(destType);
2384 unsigned size = resultBuffer.size();
2387 if (destStructType.getNumElements() != srcStructType.getNumElements())
2390 for (
auto elem :
llvm::enumerate(destStructType)) {
2391 auto structExtract =
2393 if (failed(recurse(structExtract,
2394 srcStructType.getElementType(elem.index()),
2395 destStructType.getElementType(elem.index()))))
2398 SmallVector<Value> temp(resultBuffer.begin() + size,
2399 resultBuffer.end());
2402 resultBuffer.resize(size);
2403 resultBuffer.push_back(newStruct);
2406 .Case<IntType>([&](
auto) {
2407 if (
auto result = cast(src, srcType, destType)) {
2408 resultBuffer.push_back(result);
2413 .Default([&](
auto) {
return failure(); });
2416 if (failed(recurse(array, sourceType, destType)))
2419 assert(resultBuffer.size() == 1 &&
2420 "resultBuffer must only contain a result array if `success` is true");
2421 return resultBuffer[0];
2429Value FIRRTLLowering::getLoweredAndExtendedValue(Value src, Type target) {
2430 auto srcType = cast<FIRRTLBaseType>(src.getType());
2431 auto dstType = cast<FIRRTLBaseType>(target);
2432 auto loweredSrc = getLoweredValue(src);
2435 auto dstWidth = dstType.getBitWidthOrSentinel();
2451 return getOrCreateIntConstant(dstWidth, 0);
2454 auto loweredSrcType = loweredSrc.getType();
2455 auto loweredDstType =
lowerType(dstType);
2458 if (loweredSrcType == loweredDstType)
2462 if (dstWidth == srcType.getBitWidthOrSentinel()) {
2464 if (loweredSrcType != loweredDstType &&
2465 (isa<hw::TypeAliasType>(loweredSrcType) ||
2466 isa<hw::TypeAliasType>(loweredDstType))) {
2467 return builder.createOrFold<
hw::BitcastOp>(loweredDstType, loweredSrc);
2472 if (isa<hw::ArrayType, hw::StructType>(loweredSrcType))
2473 return getExtOrTruncAggregateValue(loweredSrc, srcType, dstType,
2476 if (isa<seq::ClockType>(loweredSrcType)) {
2477 builder.emitError(
"cannot use clock type as an integer");
2481 auto intSourceType = dyn_cast<IntegerType>(loweredSrcType);
2482 if (!intSourceType) {
2483 builder.emitError(
"operand of type ")
2484 << loweredSrcType <<
" cannot be used as an integer";
2488 auto loweredSrcWidth = intSourceType.getWidth();
2489 if (loweredSrcWidth ==
unsigned(dstWidth))
2492 if (loweredSrcWidth >
unsigned(dstWidth)) {
2493 builder.emitError(
"operand should not be a truncation");
2498 auto valueFIRType = type_cast<FIRRTLBaseType>(src.getType()).getPassiveType();
2499 if (type_cast<IntType>(valueFIRType).isSigned())
2500 return comb::createOrFoldSExt(loweredSrc, loweredDstType, builder);
2502 auto zero = getOrCreateIntConstant(dstWidth - loweredSrcWidth, 0);
2511Value FIRRTLLowering::getLoweredAndExtOrTruncValue(Value value, Type destType) {
2512 assert(type_isa<FIRRTLBaseType>(value.getType()) &&
2513 type_isa<FIRRTLBaseType>(destType) &&
2514 "input/output value should be FIRRTL");
2517 auto destWidth = type_cast<FIRRTLBaseType>(destType).getBitWidthOrSentinel();
2518 if (destWidth == -1)
2521 auto result = getLoweredValue(value);
2533 return getOrCreateIntConstant(destWidth, 0);
2537 if (isa<hw::ArrayType, hw::StructType>(result.getType())) {
2539 if (destType == value.getType())
2542 return getExtOrTruncAggregateValue(
2543 result, type_cast<FIRRTLBaseType>(value.getType()),
2544 type_cast<FIRRTLBaseType>(destType),
2548 auto srcWidth = type_cast<IntegerType>(result.getType()).getWidth();
2549 if (srcWidth ==
unsigned(destWidth))
2555 if (srcWidth >
unsigned(destWidth)) {
2556 auto resultType = builder.getIntegerType(destWidth);
2560 auto resultType = builder.getIntegerType(destWidth);
2564 type_cast<FIRRTLBaseType>(value.getType()).getPassiveType();
2565 if (type_cast<IntType>(valueFIRType).isSigned())
2566 return comb::createOrFoldSExt(result, resultType, builder);
2568 auto zero = getOrCreateIntConstant(destWidth - srcWidth, 0);
2582std::optional<Value> FIRRTLLowering::getLoweredFmtOperand(Value operand) {
2584 if (type_isa<FStringType>(operand.getType())) {
2585 if (isa<TimeOp>(operand.getDefiningOp()))
2586 return sv::TimeOp::create(builder);
2587 if (isa<HierarchicalModuleNameOp>(operand.getDefiningOp()))
2591 auto loweredValue = getLoweredValue(operand);
2592 if (!loweredValue) {
2596 loweredValue = getOrCreateIntConstant(1, 0);
2601 if (
auto intTy = firrtl::type_cast<IntType>(operand.getType()))
2602 if (intTy.isSigned())
2603 loweredValue = sv::SystemFunctionOp::create(
2604 builder, loweredValue.getType(),
"signed", loweredValue);
2606 return loweredValue;
2610FIRRTLLowering::loweredFmtOperands(mlir::ValueRange operands,
2611 SmallVectorImpl<Value> &loweredOperands) {
2612 for (
auto operand : operands) {
2613 std::optional<Value> loweredValue = getLoweredFmtOperand(operand);
2618 loweredOperands.push_back(*loweredValue);
2623LogicalResult FIRRTLLowering::lowerStatementWithFd(
2624 const FileDescriptorInfo &fileDescriptor, Value clock, Value cond,
2625 const std::function<LogicalResult(Value)> &fn,
bool usePrintfCond) {
2627 bool failed =
false;
2628 circuitState.addMacroDecl(builder.getStringAttr(
"SYNTHESIS"));
2629 addToIfDefBlock(
"SYNTHESIS", std::function<
void()>(), [&]() {
2630 addToAlwaysBlock(clock, [&]() {
2633 circuitState.usedPrintf =
true;
2635 circuitState.addFragment(theModule,
"PRINTF_COND_FRAGMENT");
2638 Value ifCond = cond;
2639 if (usePrintfCond) {
2641 sv::MacroRefExprOp::create(builder, cond.getType(),
"PRINTF_COND_");
2642 ifCond = builder.createOrFold<
comb::AndOp>(ifCond, cond,
true);
2645 addIfProceduralBlock(ifCond, [&]() {
2649 if (fileDescriptor.isDefaultFd()) {
2651 fd = hw::ConstantOp::create(builder, APInt(32, 0x80000002));
2654 auto fdOrError = callFileDescriptorLib(fileDescriptor);
2655 if (llvm::failed(fdOrError)) {
2661 failed = llvm::failed(fn(fd));
2665 return failure(failed);
2669FIRRTLLowering::callFileDescriptorLib(
const FileDescriptorInfo &info) {
2670 circuitState.usedFileDescriptorLib =
true;
2671 circuitState.addFragment(theModule,
"CIRCT_LIB_LOGGING_FRAGMENT");
2674 if (
info.isSubstitutionRequired()) {
2675 SmallVector<Value> fileNameOperands;
2676 if (failed(loweredFmtOperands(
info.getSubstitutions(), fileNameOperands)))
2679 fileName = sv::SFormatFOp::create(builder,
info.getOutputFileFormat(),
2684 fileName = sv::ConstantStrOp::create(builder,
info.getOutputFileFormat())
2688 return sv::FuncCallProceduralOp::create(
2689 builder, mlir::TypeRange{builder.getIntegerType(32)},
2690 builder.getStringAttr(
"__circt_lib_logging::FileDescriptor::get"),
2691 ValueRange{fileName})
2701LogicalResult FIRRTLLowering::setLowering(Value orig, Value result) {
2702 if (
auto origType = dyn_cast<FIRRTLType>(orig.getType())) {
2703 assert((!result || !type_isa<FIRRTLType>(result.getType())) &&
2704 "Lowering didn't turn a FIRRTL value into a non-FIRRTL value");
2708 auto srcWidth = baseType.getPassiveType().getBitWidthOrSentinel();
2711 if (srcWidth != -1) {
2713 assert((srcWidth != 0) &&
2714 "Lowering produced value for zero width source");
2716 assert((srcWidth == 0) &&
2717 "Lowering produced null value but source wasn't zero width");
2721 assert(result &&
"Lowering of foreign type produced null value");
2724 auto &slot = valueMapping[orig];
2725 assert(!slot &&
"value lowered multiple times");
2732LogicalResult FIRRTLLowering::setPossiblyFoldedLowering(Value orig,
2736 if (
auto cst = dyn_cast_or_null<hw::ConstantOp>(result.getDefiningOp())) {
2737 auto &entry = hwConstantMap[cst.getValueAttr()];
2748 cst->moveBefore(&theModule.getBodyBlock()->front());
2752 return setLowering(orig, result);
2757template <
typename ResultOpType,
typename... CtorArgTypes>
2758LogicalResult FIRRTLLowering::setLoweringTo(Operation *orig,
2759 CtorArgTypes... args) {
2760 auto result = builder.createOrFold<ResultOpType>(args...);
2761 if (
auto *op = result.getDefiningOp())
2763 return setPossiblyFoldedLowering(orig->getResult(0), result);
2770template <
typename ResultOpType,
typename... CtorArgTypes>
2771LogicalResult FIRRTLLowering::setLoweringToLTL(Operation *orig,
2772 CtorArgTypes... args) {
2773 auto result = builder.createOrFold<ResultOpType>(args...);
2774 if (
auto *op = result.getDefiningOp())
2775 ltlOpFixupWorklist.insert(op);
2776 return setPossiblyFoldedLowering(orig->getResult(0), result);
2785Backedge FIRRTLLowering::createBackedge(Location loc, Type type) {
2786 auto backedge = backedgeBuilder.
get(type, loc);
2787 backedges.insert({backedge, backedge});
2795Backedge FIRRTLLowering::createBackedge(Value orig, Type type) {
2796 auto backedge = createBackedge(orig.getLoc(), type);
2797 (void)setLowering(orig, backedge);
2803bool FIRRTLLowering::updateIfBackedge(Value dest, Value src) {
2804 auto backedgeIt = backedges.find(dest);
2805 if (backedgeIt == backedges.end())
2807 backedgeIt->second = src;
2815void FIRRTLLowering::runWithInsertionPointAtEndOfBlock(
2816 const std::function<
void(
void)> &fn, Region ®ion) {
2820 auto oldIP = builder.saveInsertionPoint();
2822 builder.setInsertionPointToEnd(®ion.front());
2824 builder.restoreInsertionPoint(oldIP);
2828Value FIRRTLLowering::getReadValue(Value v) {
2829 Value result = readInOutCreated.lookup(v);
2835 auto oldIP = builder.saveInsertionPoint();
2836 if (
auto *vOp = v.getDefiningOp()) {
2837 builder.setInsertionPointAfter(vOp);
2841 builder.setInsertionPoint(&theModule.getBodyBlock()->front());
2846 if (
auto arrayIndexInout = v.getDefiningOp<sv::ArrayIndexInOutOp>()) {
2847 result = getReadValue(arrayIndexInout.getInput());
2849 arrayIndexInout.getIndex());
2854 builder.restoreInsertionPoint(oldIP);
2855 readInOutCreated.insert({v, result});
2859Value FIRRTLLowering::getNonClockValue(Value v) {
2860 auto it = fromClockMapping.try_emplace(v, Value{});
2862 ImplicitLocOpBuilder builder(v.getLoc(), v.getContext());
2863 builder.setInsertionPointAfterValue(v);
2864 it.first->second = seq::FromClockOp::create(builder, v);
2866 return it.first->second;
2869void FIRRTLLowering::addToAlwaysBlock(
2870 sv::EventControl clockEdge, Value clock, sv::ResetType resetStyle,
2871 sv::EventControl resetEdge, Value reset,
2872 const std::function<
void(
void)> &body,
2873 const std::function<
void(
void)> &resetBody) {
2874 AlwaysKeyType key{builder.getBlock(), clockEdge, clock,
2875 resetStyle, resetEdge, reset};
2876 sv::AlwaysOp alwaysOp;
2877 sv::IfOp insideIfOp;
2878 std::tie(alwaysOp, insideIfOp) = alwaysBlocks.lookup(key);
2882 assert(resetStyle != sv::ResetType::NoReset);
2895 auto createIfOp = [&]() {
2898 insideIfOp = sv::IfOp::create(
2899 builder, reset, [] {}, [] {});
2901 if (resetStyle == sv::ResetType::AsyncReset) {
2902 sv::EventControl events[] = {clockEdge, resetEdge};
2903 Value clocks[] = {clock, reset};
2905 alwaysOp = sv::AlwaysOp::create(builder, events, clocks, [&]() {
2906 if (resetEdge == sv::EventControl::AtNegEdge)
2907 llvm_unreachable(
"negative edge for reset is not expected");
2911 alwaysOp = sv::AlwaysOp::create(builder, clockEdge, clock, createIfOp);
2915 alwaysOp = sv::AlwaysOp::create(builder, clockEdge, clock);
2916 insideIfOp =
nullptr;
2918 alwaysBlocks[key] = {alwaysOp, insideIfOp};
2922 assert(insideIfOp &&
"reset body must be initialized before");
2923 runWithInsertionPointAtEndOfBlock(resetBody, insideIfOp.getThenRegion());
2924 runWithInsertionPointAtEndOfBlock(body, insideIfOp.getElseRegion());
2926 runWithInsertionPointAtEndOfBlock(body, alwaysOp.getBody());
2932 alwaysOp->moveBefore(builder.getInsertionBlock(),
2933 builder.getInsertionPoint());
2936LogicalResult FIRRTLLowering::emitGuards(Location loc,
2937 ArrayRef<Attribute> guards,
2938 std::function<
void(
void)>
emit) {
2939 if (guards.empty()) {
2943 auto guard = dyn_cast<StringAttr>(guards[0]);
2945 return mlir::emitError(loc,
2946 "elements in `guards` array must be `StringAttr`");
2949 circuitState.addMacroDecl(builder.getStringAttr(guard.getValue()));
2950 LogicalResult result = LogicalResult::failure();
2951 addToIfDefBlock(guard.getValue(), [&]() {
2952 result = emitGuards(loc, guards.drop_front(), emit);
2957void FIRRTLLowering::addToIfDefBlock(StringRef cond,
2958 std::function<
void(
void)> thenCtor,
2959 std::function<
void(
void)> elseCtor) {
2960 auto condAttr = builder.getStringAttr(cond);
2961 auto op = ifdefBlocks.lookup({builder.getBlock(), condAttr});
2963 runWithInsertionPointAtEndOfBlock(thenCtor, op.getThenRegion());
2964 runWithInsertionPointAtEndOfBlock(elseCtor, op.getElseRegion());
2969 op->moveBefore(builder.getInsertionBlock(), builder.getInsertionPoint());
2971 ifdefBlocks[{builder.getBlock(), condAttr}] =
2972 sv::IfDefOp::create(builder, condAttr, thenCtor, elseCtor);
2976void FIRRTLLowering::addToInitialBlock(std::function<
void(
void)> body) {
2977 auto op = initialBlocks.lookup(builder.getBlock());
2979 runWithInsertionPointAtEndOfBlock(body, op.getBody());
2984 op->moveBefore(builder.getInsertionBlock(), builder.getInsertionPoint());
2986 initialBlocks[builder.getBlock()] = sv::InitialOp::create(builder, body);
2990void FIRRTLLowering::addIfProceduralBlock(Value cond,
2991 std::function<
void(
void)> thenCtor,
2992 std::function<
void(
void)> elseCtor) {
2995 auto insertIt = builder.getInsertionPoint();
2996 if (insertIt != builder.getBlock()->begin())
2997 if (
auto ifOp = dyn_cast<sv::IfOp>(*--insertIt)) {
2998 if (ifOp.getCond() == cond) {
2999 runWithInsertionPointAtEndOfBlock(thenCtor, ifOp.getThenRegion());
3000 runWithInsertionPointAtEndOfBlock(elseCtor, ifOp.getElseRegion());
3005 sv::IfOp::create(builder, cond, thenCtor, elseCtor);
3017FIRRTLLowering::UnloweredOpResult
3018FIRRTLLowering::handleUnloweredOp(Operation *op) {
3020 if (!op->getRegions().empty() &&
3021 isa_and_nonnull<FIRRTLDialect>(op->getDialect())) {
3022 op->emitOpError(
"must explicitly handle its regions");
3023 return LoweringFailure;
3030 if (!isa_and_nonnull<FIRRTLDialect>(op->getDialect())) {
3032 for (
auto ®ion : op->getRegions())
3033 addToWorklist(region);
3034 for (
auto &operand : op->getOpOperands())
3035 if (auto lowered = getPossiblyInoutLoweredValue(operand.
get()))
3036 operand.set(lowered);
3037 for (
auto result : op->getResults())
3038 (void)setLowering(result, result);
3039 return AlreadyLowered;
3051 if (op->getNumResults() == 1) {
3052 auto resultType = op->getResult(0).getType();
3053 if (type_isa<FIRRTLBaseType>(resultType) &&
3055 (
isExpression(op) || isa<mlir::UnrealizedConversionCastOp>(op))) {
3057 (void)setLowering(op->getResult(0), Value());
3061 op->emitOpError(
"LowerToHW couldn't handle this operation");
3062 return LoweringFailure;
3065LogicalResult FIRRTLLowering::visitExpr(ConstantOp op) {
3068 return setLowering(op, Value());
3070 return setLowering(op, getOrCreateIntConstant(op.getValue()));
3073LogicalResult FIRRTLLowering::visitExpr(SpecialConstantOp op) {
3075 if (isa<ClockType>(op.getType())) {
3076 cst = getOrCreateClockConstant(op.getValue() ? seq::ClockConst::High
3077 :
seq::ClockConst::Low);
3079 cst = getOrCreateIntConstant(APInt( 1, op.getValue()));
3081 return setLowering(op, cst);
3084FailureOr<Value> FIRRTLLowering::lowerSubindex(SubindexOp op, Value input) {
3085 auto iIdx = getOrCreateIntConstant(
3087 firrtl::type_cast<FVectorType>(op.getInput().getType())
3094 if (isa<sv::InOutType>(input.getType()))
3095 result = builder.createOrFold<sv::ArrayIndexInOutOp>(input, iIdx);
3098 if (
auto *definingOp = result.getDefiningOp())
3103FailureOr<Value> FIRRTLLowering::lowerSubaccess(SubaccessOp op, Value input) {
3104 Value valueIdx = getLoweredAndExtOrTruncValue(
3106 UIntType::get(op->getContext(),
3108 firrtl::type_cast<FVectorType>(op.getInput().getType())
3109 .getNumElements())));
3111 op->emitError() <<
"input lowering failed";
3118 if (isa<sv::InOutType>(input.getType()))
3119 result = builder.createOrFold<sv::ArrayIndexInOutOp>(input, valueIdx);
3121 result = createArrayIndexing(input, valueIdx);
3122 if (
auto *definingOp = result.getDefiningOp())
3127FailureOr<Value> FIRRTLLowering::lowerSubfield(SubfieldOp op, Value input) {
3128 auto resultType =
lowerType(op->getResult(0).getType());
3129 if (!resultType || !input) {
3130 op->emitError() <<
"subfield type lowering failed";
3136 auto field = firrtl::type_cast<BundleType>(op.getInput().getType())
3137 .getElementName(op.getFieldIndex());
3139 if (isa<sv::InOutType>(input.getType()))
3140 result = builder.createOrFold<sv::StructFieldInOutOp>(input, field);
3143 if (
auto *definingOp = result.getDefiningOp())
3148LogicalResult FIRRTLLowering::visitExpr(SubindexOp op) {
3150 return setLowering(op, Value());
3152 auto input = getPossiblyInoutLoweredValue(op.getInput());
3154 return op.emitError() <<
"input lowering failed";
3156 auto result = lowerSubindex(op, input);
3159 return setLowering(op, *result);
3162LogicalResult FIRRTLLowering::visitExpr(SubaccessOp op) {
3164 return setLowering(op, Value());
3166 auto input = getPossiblyInoutLoweredValue(op.getInput());
3168 return op.emitError() <<
"input lowering failed";
3170 auto result = lowerSubaccess(op, input);
3173 return setLowering(op, *result);
3176LogicalResult FIRRTLLowering::visitExpr(SubfieldOp op) {
3179 if (getLoweredValue(op) || !op.getInput())
3183 return setLowering(op, Value());
3185 auto input = getPossiblyInoutLoweredValue(op.getInput());
3187 return op.emitError() <<
"input lowering failed";
3189 auto result = lowerSubfield(op, input);
3192 return setLowering(op, *result);
3195LogicalResult FIRRTLLowering::visitExpr(VectorCreateOp op) {
3196 auto resultType =
lowerType(op.getResult().getType());
3197 SmallVector<Value> operands;
3199 for (
auto oper :
llvm::reverse(op.getOperands())) {
3200 auto val = getLoweredValue(oper);
3203 operands.push_back(val);
3205 return setLoweringTo<hw::ArrayCreateOp>(op, resultType, operands);
3208LogicalResult FIRRTLLowering::visitExpr(BundleCreateOp op) {
3209 auto resultType =
lowerType(op.getResult().getType());
3210 SmallVector<Value> operands;
3211 for (
auto oper : op.getOperands()) {
3212 auto val = getLoweredValue(oper);
3215 operands.push_back(val);
3217 return setLoweringTo<hw::StructCreateOp>(op, resultType, operands);
3220LogicalResult FIRRTLLowering::visitExpr(FEnumCreateOp op) {
3223 return setLowering(op, Value());
3225 auto input = getLoweredValue(op.getInput());
3226 auto tagName = op.getFieldNameAttr();
3227 auto oldType = op.getType().base();
3229 auto element = *oldType.getElement(op.getFieldNameAttr());
3231 if (
auto structType = dyn_cast<hw::StructType>(newType)) {
3232 auto tagType = structType.getFieldType(
"tag");
3233 auto tagValue = IntegerAttr::get(tagType, element.value.getValue());
3234 auto tag = sv::LocalParamOp::create(builder, op.getLoc(), tagType, tagValue,
3236 auto bodyType = structType.getFieldType(
"body");
3237 auto body = hw::UnionCreateOp::create(builder, bodyType, tagName, input);
3238 SmallVector<Value> operands = {tag.getResult(), body.getResult()};
3239 return setLoweringTo<hw::StructCreateOp>(op, structType, operands);
3241 auto tagValue = IntegerAttr::get(newType, element.value.getValue());
3242 return setLoweringTo<sv::LocalParamOp>(op, newType, tagValue, tagName);
3245LogicalResult FIRRTLLowering::visitExpr(AggregateConstantOp op) {
3246 auto resultType =
lowerType(op.getResult().getType());
3248 getOrCreateAggregateConstantAttribute(op.getFieldsAttr(), resultType);
3250 return setLoweringTo<hw::AggregateConstantOp>(op, resultType,
3251 cast<ArrayAttr>(attr));
3254LogicalResult FIRRTLLowering::visitExpr(IsTagOp op) {
3256 auto tagName = op.getFieldNameAttr();
3257 auto lhs = getLoweredValue(op.getInput());
3258 if (isa<hw::StructType>(lhs.getType()))
3261 auto index = op.getFieldIndex();
3262 auto enumType = op.getInput().getType().base();
3263 auto tagValue = enumType.getElementValueAttr(index);
3264 auto tagValueType = IntegerType::get(op.getContext(), enumType.getTagWidth());
3265 auto loweredTagValue = IntegerAttr::get(tagValueType, tagValue.getValue());
3266 auto rhs = sv::LocalParamOp::create(builder, op.getLoc(), tagValueType,
3267 loweredTagValue, tagName);
3269 Type resultType = builder.getIntegerType(1);
3270 return setLoweringTo<comb::ICmpOp>(op, resultType, ICmpPredicate::eq, lhs,
3274LogicalResult FIRRTLLowering::visitExpr(SubtagOp op) {
3277 return setLowering(op, Value());
3279 auto tagName = op.getFieldNameAttr();
3280 auto input = getLoweredValue(op.getInput());
3282 return setLoweringTo<hw::UnionExtractOp>(op, field, tagName);
3285LogicalResult FIRRTLLowering::visitExpr(TagExtractOp op) {
3288 return setLowering(op, Value());
3290 auto input = getLoweredValue(op.getInput());
3296 if (isa<hw::StructType>(input.getType())) {
3297 return setLoweringTo<hw::StructExtractOp>(op, input,
"tag");
3302 return setLowering(op, input);
3309LogicalResult FIRRTLLowering::visitDecl(WireOp op) {
3310 auto origResultType = op.getResult().getType();
3314 if (!type_isa<FIRRTLType>(origResultType)) {
3315 createBackedge(op.getResult(), origResultType);
3319 auto resultType =
lowerType(origResultType);
3323 if (resultType.isInteger(0)) {
3324 if (op.getInnerSym())
3325 return op.emitError(
"zero width wire is referenced by name [")
3326 << *op.getInnerSym() <<
"] (e.g. in an XMR) but must be removed";
3327 return setLowering(op.getResult(), Value());
3331 auto innerSym = lowerInnerSymbol(op);
3332 auto name = op.getNameAttr();
3335 auto wire = hw::WireOp::create(
3336 builder, op.getLoc(), getOrCreateZConstant(resultType), name, innerSym);
3338 if (
auto svAttrs = sv::getSVAttributes(op))
3339 sv::setSVAttributes(wire, svAttrs);
3341 return setLowering(op.getResult(), wire);
3344LogicalResult FIRRTLLowering::visitDecl(VerbatimWireOp op) {
3345 auto resultTy =
lowerType(op.getType());
3348 resultTy = sv::InOutType::get(op.getContext(), resultTy);
3350 SmallVector<Value, 4> operands;
3351 operands.reserve(op.getSubstitutions().size());
3352 for (
auto operand : op.getSubstitutions()) {
3353 auto lowered = getLoweredValue(operand);
3356 operands.push_back(lowered);
3359 ArrayAttr symbols = op.getSymbolsAttr();
3361 symbols = ArrayAttr::get(op.getContext(), {});
3363 return setLoweringTo<sv::VerbatimExprSEOp>(op, resultTy, op.getTextAttr(),
3367LogicalResult FIRRTLLowering::visitDecl(NodeOp op) {
3368 auto operand = getLoweredValue(op.getInput());
3370 return handleZeroBit(op.getInput(), [&]() -> LogicalResult {
3371 if (op.getInnerSym())
3372 return op.emitError(
"zero width node is referenced by name [")
3373 << *op.getInnerSym()
3374 <<
"] (e.g. in an XMR) but must be "
3376 return setLowering(op.getResult(), Value());
3382 auto name = op.getNameAttr();
3383 auto innerSym = lowerInnerSymbol(op);
3386 operand = hw::WireOp::create(builder, operand, name, innerSym);
3389 if (
auto svAttrs = sv::getSVAttributes(op)) {
3391 operand = hw::WireOp::create(builder, operand, name);
3392 sv::setSVAttributes(operand.getDefiningOp(), svAttrs);
3395 return setLowering(op.getResult(), operand);
3398LogicalResult FIRRTLLowering::visitDecl(RegOp op) {
3399 auto resultType =
lowerType(op.getResult().getType());
3402 if (resultType.isInteger(0))
3403 return setLowering(op.getResult(), Value());
3405 Value clockVal = getLoweredValue(op.getClockVal());
3410 auto innerSym = lowerInnerSymbol(op);
3411 Backedge inputEdge = backedgeBuilder.
get(resultType);
3412 auto reg = seq::FirRegOp::create(builder, inputEdge, clockVal,
3413 op.getNameAttr(), innerSym);
3416 if (
auto randomRegister = op->getAttr(
"firrtl.random_init_register"))
3417 reg->setAttr(
"firrtl.random_init_register", randomRegister);
3418 if (
auto randomStart = op->getAttr(
"firrtl.random_init_start"))
3419 reg->setAttr(
"firrtl.random_init_start", randomStart);
3420 if (
auto randomEnd = op->getAttr(
"firrtl.random_init_end"))
3421 reg->setAttr(
"firrtl.random_init_end", randomEnd);
3424 if (
auto svAttrs = sv::getSVAttributes(op))
3425 sv::setSVAttributes(reg, svAttrs);
3428 (void)setLowering(op.getResult(),
reg);
3432LogicalResult FIRRTLLowering::visitDecl(RegResetOp op) {
3433 auto resultType =
lowerType(op.getResult().getType());
3436 if (resultType.isInteger(0))
3437 return setLowering(op.getResult(), Value());
3439 Value clockVal = getLoweredValue(op.getClockVal());
3440 Value resetSignal = getLoweredValue(op.getResetSignal());
3442 Value resetValue = getLoweredAndExtOrTruncValue(
3443 op.getResetValue(), type_cast<FIRRTLBaseType>(op.getResult().getType()));
3445 if (!clockVal || !resetSignal || !resetValue)
3449 auto innerSym = lowerInnerSymbol(op);
3450 bool isAsync = type_isa<AsyncResetType>(op.getResetSignal().getType());
3451 Backedge inputEdge = backedgeBuilder.
get(resultType);
3453 seq::FirRegOp::create(builder, inputEdge, clockVal, op.getNameAttr(),
3454 resetSignal, resetValue, innerSym, isAsync);
3457 if (
auto randomRegister = op->getAttr(
"firrtl.random_init_register"))
3458 reg->setAttr(
"firrtl.random_init_register", randomRegister);
3459 if (
auto randomStart = op->getAttr(
"firrtl.random_init_start"))
3460 reg->setAttr(
"firrtl.random_init_start", randomStart);
3461 if (
auto randomEnd = op->getAttr(
"firrtl.random_init_end"))
3462 reg->setAttr(
"firrtl.random_init_end", randomEnd);
3465 if (
auto svAttrs = sv::getSVAttributes(op))
3466 sv::setSVAttributes(reg, svAttrs);
3469 (void)setLowering(op.getResult(),
reg);
3474LogicalResult FIRRTLLowering::visitDecl(MemOp op) {
3477 if (type_isa<BundleType>(op.getDataType()))
3478 return op.emitOpError(
3479 "should have already been lowered from a ground type to an aggregate "
3480 "type using the LowerTypes pass. Use "
3481 "'firtool --lower-types' or 'circt-opt "
3482 "--pass-pipeline='firrtl.circuit(firrtl-lower-types)' "
3488 auto memType = seq::FirMemType::get(
3491 : std::optional<uint32_t>());
3493 seq::FirMemInitAttr memInit;
3494 if (
auto init = op.getInitAttr())
3495 memInit = seq::FirMemInitAttr::get(init.getContext(), init.getFilename(),
3496 init.getIsBinary(), init.getIsInline());
3498 auto memDecl = seq::FirMemOp::create(
3501 op.getInnerSymAttr(), memInit, op.getPrefixAttr(), Attribute{});
3504 if (
auto file = parent->getAttrOfType<hw::OutputFileAttr>(
"output_file")) {
3506 if (!file.isDirectory())
3507 dir = hw::OutputFileAttr::getAsDirectory(builder.getContext(),
3508 file.getDirectory());
3509 memDecl.setOutputFileAttr(dir);
3515 for (
size_t i = 0, e = op.getNumResults(); i != e; ++i) {
3517 auto addOutput = [&](StringRef field,
size_t width, Value value) {
3520 (void)setLowering(a, value);
3526 auto addInput = [&](StringRef field, Value backedge) {
3528 if (cast<FIRRTLBaseType>(a.getType())
3530 .getBitWidthOrSentinel() > 0)
3531 (void)setLowering(a, backedge);
3537 auto addInputPort = [&](StringRef field,
size_t width) -> Value {
3541 Value backedge, portValue;
3543 portValue = getOrCreateXConstant(1);
3545 auto portType = IntegerType::get(op.getContext(), width);
3546 backedge = portValue = createBackedge(builder.getLoc(), portType);
3548 addInput(field, backedge);
3552 auto addClock = [&](StringRef field) -> Value {
3553 Type clockTy = seq::ClockType::get(op.getContext());
3554 Value portValue = createBackedge(builder.getLoc(), clockTy);
3555 addInput(field, portValue);
3559 auto memportKind = op.getPortKind(i);
3560 if (memportKind == MemOp::PortKind::Read) {
3561 auto addr = addInputPort(
"addr", op.getAddrBits());
3562 auto en = addInputPort(
"en", 1);
3563 auto clk = addClock(
"clk");
3564 auto data = seq::FirMemReadOp::create(builder, memDecl,
addr,
clk,
en);
3566 }
else if (memportKind == MemOp::PortKind::ReadWrite) {
3567 auto addr = addInputPort(
"addr", op.getAddrBits());
3568 auto en = addInputPort(
"en", 1);
3569 auto clk = addClock(
"clk");
3572 auto mode = addInputPort(
"wmode", 1);
3574 mode = builder.createOrFold<
comb::AndOp>(mode, addInputPort(
"wmask", 1),
3581 auto rdata = seq::FirMemReadWriteOp::create(builder, memDecl,
addr,
clk,
3585 auto addr = addInputPort(
"addr", op.getAddrBits());
3588 auto en = addInputPort(
"en", 1);
3592 auto clk = addClock(
"clk");
3605LogicalResult FIRRTLLowering::visitDecl(InstanceOp oldInstance) {
3606 Operation *oldModule =
3607 oldInstance.getReferencedModule(circuitState.getInstanceGraph());
3609 auto *newModule = circuitState.getNewModule(oldModule);
3611 oldInstance->emitOpError(
"could not find module [")
3612 << oldInstance.getModuleName() <<
"] referenced by instance";
3618 ArrayAttr parameters;
3619 if (
auto oldExtModule = dyn_cast<FExtModuleOp>(oldModule))
3624 SmallVector<PortInfo, 8> portInfo = cast<FModuleLike>(oldModule).getPorts();
3629 for (
unsigned portIdx = 0, e = portInfo.size(); portIdx != e; ++portIdx)
3630 portIndicesByName[portInfo[portIdx].name] = portIdx;
3634 SmallVector<Value, 8> operands;
3635 for (
size_t portIndex = 0, e = portInfo.size(); portIndex != e; ++portIndex) {
3636 auto &port = portInfo[portIndex];
3639 oldInstance->emitOpError(
"could not lower type of port ") << port.name;
3644 if (portType.isInteger(0))
3648 if (port.isOutput())
3651 auto portResult = oldInstance.getResult(portIndex);
3652 assert(portResult &&
"invalid IR, couldn't find port");
3656 if (port.isInput()) {
3657 operands.push_back(createBackedge(portResult, portType));
3663 if (type_isa<AnalogType>(portResult.getType()) && portResult.hasOneUse()) {
3664 if (
auto attach = dyn_cast<AttachOp>(*portResult.getUsers().begin())) {
3666 auto loweredResult = getPossiblyInoutLoweredValue(source);
3667 operands.push_back(loweredResult);
3668 (void)setLowering(portResult, loweredResult);
3677 "." + port.getName().str() +
".wire");
3681 (void)setLowering(portResult, wire);
3683 operands.push_back(wire);
3690 auto innerSym = oldInstance.getInnerSymAttr();
3691 if (oldInstance.getLowerToBind()) {
3694 oldInstance.getContext(), oldInstance.getInnerSymAttr(), 0,
3697 auto bindOp = sv::BindOp::create(builder, theModule.getNameAttr(),
3698 innerSym.getSymName());
3701 if (
auto outputFile = oldInstance->getAttr(
"output_file"))
3702 bindOp->setAttr(
"output_file", outputFile);
3705 circuitState.addBind(bindOp);
3710 hw::InstanceOp::create(builder, newModule, oldInstance.getNameAttr(),
3711 operands, parameters, innerSym);
3713 if (oldInstance.getLowerToBind() || oldInstance.getDoNotPrint())
3714 newInstance.setDoNotPrintAttr(builder.getUnitAttr());
3716 if (newInstance.getInnerSymAttr())
3717 if (
auto forceName = circuitState.instanceForceNames.lookup(
3718 {newInstance->getParentOfType<hw::HWModuleOp>().getNameAttr(),
3719 newInstance.getInnerNameAttr()}))
3720 newInstance->setAttr(
"hw.verilogName", forceName);
3724 unsigned resultNo = 0;
3725 for (
size_t portIndex = 0, e = portInfo.size(); portIndex != e; ++portIndex) {
3726 auto &port = portInfo[portIndex];
3730 Value resultVal = newInstance.getResult(resultNo);
3732 auto oldPortResult = oldInstance.getResult(portIndex);
3733 (void)setLowering(oldPortResult, resultVal);
3739LogicalResult FIRRTLLowering::visitDecl(ContractOp oldOp) {
3740 SmallVector<Value> inputs;
3741 SmallVector<Type> types;
3742 for (
auto input : oldOp.getInputs()) {
3743 auto lowered = getLoweredValue(input);
3746 inputs.push_back(lowered);
3747 types.push_back(lowered.getType());
3750 auto newOp = verif::ContractOp::create(builder, types, inputs);
3751 newOp->setDiscardableAttrs(oldOp->getDiscardableAttrDictionary());
3752 auto &body = newOp.getBody().emplaceBlock();
3754 for (
auto [newResult, oldResult, oldArg] :
3755 llvm::zip(newOp.getResults(), oldOp.getResults(),
3756 oldOp.getBody().getArguments())) {
3757 if (failed(setLowering(oldResult, newResult)))
3759 if (failed(setLowering(oldArg, newResult)))
3763 body.getOperations().splice(body.end(),
3764 oldOp.getBody().front().getOperations());
3765 addToWorklist(body);
3775LogicalResult FIRRTLLowering::lowerNoopCast(Operation *op) {
3776 auto operand = getPossiblyInoutLoweredValue(op->getOperand(0));
3781 return setLowering(op->getResult(0), operand);
3784LogicalResult FIRRTLLowering::visitExpr(AsSIntPrimOp op) {
3785 if (isa<ClockType>(op.getInput().getType()))
3786 return setLowering(op->getResult(0),
3787 getLoweredNonClockValue(op.getInput()));
3788 return lowerNoopCast(op);
3791LogicalResult FIRRTLLowering::visitExpr(AsUIntPrimOp op) {
3792 if (isa<ClockType>(op.getInput().getType()))
3793 return setLowering(op->getResult(0),
3794 getLoweredNonClockValue(op.getInput()));
3795 return lowerNoopCast(op);
3798LogicalResult FIRRTLLowering::visitExpr(AsClockPrimOp op) {
3799 return setLoweringTo<seq::ToClockOp>(op, getLoweredValue(op.getInput()));
3802LogicalResult FIRRTLLowering::visitUnrealizedConversionCast(
3803 mlir::UnrealizedConversionCastOp op) {
3805 if (op.getNumOperands() != 1 || op.getNumResults() != 1)
3808 auto operand = op.getOperand(0);
3809 auto result = op.getResult(0);
3812 if (type_isa<FIRRTLType>(operand.getType()) &&
3813 type_isa<FIRRTLType>(result.getType()))
3814 return lowerNoopCast(op);
3818 if (!type_isa<FIRRTLType>(operand.getType())) {
3819 if (type_isa<FIRRTLType>(result.getType()))
3820 return setLowering(result, getPossiblyInoutLoweredValue(operand));
3826 auto loweredResult = getLoweredValue(operand);
3827 if (!loweredResult) {
3830 if (operand.getType().isSignlessInteger(0)) {
3831 return setLowering(result, Value());
3838 result.replaceAllUsesWith(loweredResult);
3842LogicalResult FIRRTLLowering::visitExpr(HWStructCastOp op) {
3845 if (
auto opStructType = dyn_cast<hw::StructType>(op.getOperand().getType()))
3846 return setLowering(op, op.getOperand());
3850 auto result = getLoweredValue(op.getOperand());
3856 op.replaceAllUsesWith(result);
3860LogicalResult FIRRTLLowering::visitExpr(BitCastOp op) {
3861 auto operand = getLoweredValue(op.getOperand());
3864 auto resultType =
lowerType(op.getType());
3868 return setLoweringTo<hw::BitcastOp>(op, resultType, operand);
3871LogicalResult FIRRTLLowering::visitExpr(CvtPrimOp op) {
3872 auto operand = getLoweredValue(op.getOperand());
3876 if (type_cast<IntType>(op.getOperand().getType()).isUnsigned())
3877 return setLowering(op, getOrCreateIntConstant(1, 0));
3879 return setLowering(op, Value());
3884 if (type_cast<IntType>(op.getOperand().getType()).isSigned())
3885 return setLowering(op, operand);
3888 auto zero = getOrCreateIntConstant(1, 0);
3889 return setLoweringTo<comb::ConcatOp>(op, zero, operand);
3892LogicalResult FIRRTLLowering::visitExpr(NotPrimOp op) {
3893 auto operand = getLoweredValue(op.getInput());
3897 auto allOnes = getOrCreateIntConstant(
3898 APInt::getAllOnes(operand.getType().getIntOrFloatBitWidth()));
3899 return setLoweringTo<comb::XorOp>(op, operand, allOnes,
true);
3902LogicalResult FIRRTLLowering::visitExpr(NegPrimOp op) {
3905 auto operand = getLoweredAndExtendedValue(op.getInput(), op.getType());
3909 auto resultType =
lowerType(op.getType());
3911 auto zero = getOrCreateIntConstant(resultType.getIntOrFloatBitWidth(), 0);
3912 return setLoweringTo<comb::SubOp>(op, zero, operand,
true);
3916LogicalResult FIRRTLLowering::visitExpr(PadPrimOp op) {
3917 auto operand = getLoweredAndExtendedValue(op.getInput(), op.getType());
3920 return setLowering(op, operand);
3923LogicalResult FIRRTLLowering::visitExpr(XorRPrimOp op) {
3924 auto operand = getLoweredValue(op.getInput());
3927 return setLowering(op, getOrCreateIntConstant(1, 0));
3932 return setLoweringTo<comb::ParityOp>(op, builder.getIntegerType(1), operand,
3936LogicalResult FIRRTLLowering::visitExpr(AndRPrimOp op) {
3937 auto operand = getLoweredValue(op.getInput());
3940 return setLowering(op, getOrCreateIntConstant(1, 1));
3945 return setLoweringTo<comb::ICmpOp>(
3946 op, ICmpPredicate::eq, operand,
3947 getOrCreateIntConstant(
3948 APInt::getAllOnes(operand.getType().getIntOrFloatBitWidth())),
3952LogicalResult FIRRTLLowering::visitExpr(OrRPrimOp op) {
3953 auto operand = getLoweredValue(op.getInput());
3956 return setLowering(op, getOrCreateIntConstant(1, 0));
3962 return setLoweringTo<comb::ICmpOp>(
3963 op, ICmpPredicate::ne, operand,
3964 getOrCreateIntConstant(operand.getType().getIntOrFloatBitWidth(), 0),
3972template <
typename ResultOpType>
3973LogicalResult FIRRTLLowering::lowerBinOpToVariadic(Operation *op) {
3974 auto resultType = op->getResult(0).getType();
3975 auto lhs = getLoweredAndExtendedValue(op->getOperand(0), resultType);
3976 auto rhs = getLoweredAndExtendedValue(op->getOperand(1), resultType);
3980 return setLoweringTo<ResultOpType>(op, lhs, rhs,
true);
3986template <
typename ResultOpType>
3987LogicalResult FIRRTLLowering::lowerElementwiseLogicalOp(Operation *op) {
3988 auto resultType = op->getResult(0).getType();
3989 auto lhs = getLoweredAndExtendedValue(op->getOperand(0), resultType);
3990 auto rhs = getLoweredAndExtendedValue(op->getOperand(1), resultType);
4001 auto intType = builder.getIntegerType(*bitwidth);
4002 auto retType = lhs.getType();
4005 auto result = builder.createOrFold<ResultOpType>(lhs, rhs,
true);
4006 return setLoweringTo<hw::BitcastOp>(op, retType, result);
4011template <
typename ResultUn
signedOpType,
typename ResultSignedOpType>
4012LogicalResult FIRRTLLowering::lowerBinOp(Operation *op) {
4014 auto resultType = op->getResult(0).getType();
4015 auto lhs = getLoweredAndExtendedValue(op->getOperand(0), resultType);
4016 auto rhs = getLoweredAndExtendedValue(op->getOperand(1), resultType);
4021 if (type_cast<IntType>(resultType).isSigned())
4022 return setLoweringTo<ResultSignedOpType>(op, lhs, rhs,
true);
4023 return setLoweringTo<ResultUnsignedOpType>(op, lhs, rhs,
true);
4028LogicalResult FIRRTLLowering::lowerCmpOp(Operation *op, ICmpPredicate signedOp,
4029 ICmpPredicate unsignedOp) {
4031 auto lhsIntType = type_cast<IntType>(op->getOperand(0).getType());
4032 auto rhsIntType = type_cast<IntType>(op->getOperand(1).getType());
4033 if (!lhsIntType.hasWidth() || !rhsIntType.hasWidth())
4037 if (cmpType.getWidth() == 0)
4038 cmpType = UIntType::get(builder.getContext(), 1);
4039 auto lhs = getLoweredAndExtendedValue(op->getOperand(0), cmpType);
4040 auto rhs = getLoweredAndExtendedValue(op->getOperand(1), cmpType);
4045 Type resultType = builder.getIntegerType(1);
4046 return setLoweringTo<comb::ICmpOp>(
4047 op, resultType, lhsIntType.isSigned() ? signedOp : unsignedOp, lhs, rhs,
4053template <
typename SignedOp,
typename Un
signedOp>
4054LogicalResult FIRRTLLowering::lowerDivLikeOp(Operation *op) {
4058 auto opType = type_cast<IntType>(op->getResult(0).getType());
4059 if (opType.getWidth() == 0)
4060 return setLowering(op->getResult(0), Value());
4064 auto lhs = getLoweredAndExtendedValue(op->getOperand(0), resultType);
4065 auto rhs = getLoweredAndExtendedValue(op->getOperand(1), resultType);
4070 if (opType.isSigned())
4071 result = builder.createOrFold<SignedOp>(lhs, rhs,
true);
4073 result = builder.createOrFold<UnsignedOp>(lhs, rhs,
true);
4075 if (
auto *definingOp = result.getDefiningOp())
4078 if (resultType == opType)
4079 return setLowering(op->getResult(0), result);
4080 return setLoweringTo<comb::ExtractOp>(op,
lowerType(opType), result, 0);
4083LogicalResult FIRRTLLowering::visitExpr(CatPrimOp op) {
4085 if (op.getInputs().empty())
4086 return setLowering(op, Value());
4088 SmallVector<Value> loweredOperands;
4091 for (
auto operand : op.getInputs()) {
4092 auto loweredOperand = getLoweredValue(operand);
4093 if (loweredOperand) {
4094 loweredOperands.push_back(loweredOperand);
4097 auto result =
handleZeroBit(operand, [&]() {
return success(); });
4105 if (loweredOperands.empty())
4106 return setLowering(op, Value());
4109 return setLoweringTo<comb::ConcatOp>(op, loweredOperands);
4116LogicalResult FIRRTLLowering::visitExpr(IsXIntrinsicOp op) {
4117 auto input = getLoweredNonClockValue(op.getArg());
4121 if (!isa<IntType>(input.getType())) {
4122 auto srcType = op.getArg().getType();
4124 assert(bitwidth &&
"Unknown width");
4125 auto intType = builder.getIntegerType(*bitwidth);
4126 input = builder.createOrFold<
hw::BitcastOp>(intType, input);
4129 return setLoweringTo<comb::ICmpOp>(
4130 op, ICmpPredicate::ceq, input,
4131 getOrCreateXConstant(input.getType().getIntOrFloatBitWidth()),
true);
4134LogicalResult FIRRTLLowering::visitStmt(FPGAProbeIntrinsicOp op) {
4135 auto operand = getLoweredValue(op.getInput());
4136 hw::WireOp::create(builder, operand);
4140LogicalResult FIRRTLLowering::visitExpr(PlusArgsTestIntrinsicOp op) {
4141 return setLoweringTo<sim::PlusArgsTestOp>(op, builder.getIntegerType(1),
4142 op.getFormatStringAttr());
4145LogicalResult FIRRTLLowering::visitExpr(PlusArgsValueIntrinsicOp op) {
4146 auto type =
lowerType(op.getResult().getType());
4150 auto valueOp = sim::PlusArgsValueOp::create(
4151 builder, builder.getIntegerType(1), type, op.getFormatStringAttr());
4152 if (failed(setLowering(op.getResult(), valueOp.getResult())))
4154 if (failed(setLowering(op.getFound(), valueOp.getFound())))
4159LogicalResult FIRRTLLowering::visitExpr(SizeOfIntrinsicOp op) {
4160 op.emitError(
"SizeOf should have been resolved.");
4164LogicalResult FIRRTLLowering::visitExpr(ClockGateIntrinsicOp op) {
4166 if (op.getTestEnable())
4167 testEnable = getLoweredValue(op.getTestEnable());
4168 return setLoweringTo<seq::ClockGateOp>(
4169 op, getLoweredValue(op.getInput()), getLoweredValue(op.getEnable()),
4170 testEnable, hw::InnerSymAttr{});
4173LogicalResult FIRRTLLowering::visitExpr(ClockInverterIntrinsicOp op) {
4174 auto operand = getLoweredValue(op.getInput());
4175 return setLoweringTo<seq::ClockInverterOp>(op, operand);
4178LogicalResult FIRRTLLowering::visitExpr(ClockDividerIntrinsicOp op) {
4179 auto operand = getLoweredValue(op.getInput());
4180 return setLoweringTo<seq::ClockDividerOp>(op, operand, op.getPow2());
4183LogicalResult FIRRTLLowering::visitExpr(LTLAndIntrinsicOp op) {
4184 return setLoweringToLTL<ltl::AndOp>(
4186 ValueRange{getLoweredValue(op.getLhs()), getLoweredValue(op.getRhs())});
4189LogicalResult FIRRTLLowering::visitExpr(LTLOrIntrinsicOp op) {
4190 return setLoweringToLTL<ltl::OrOp>(
4192 ValueRange{getLoweredValue(op.getLhs()), getLoweredValue(op.getRhs())});
4195LogicalResult FIRRTLLowering::visitExpr(LTLIntersectIntrinsicOp op) {
4196 return setLoweringToLTL<ltl::IntersectOp>(
4198 ValueRange{getLoweredValue(op.getLhs()), getLoweredValue(op.getRhs())});
4201LogicalResult FIRRTLLowering::visitExpr(LTLDelayIntrinsicOp op) {
4202 return setLoweringToLTL<ltl::DelayOp>(op, getLoweredValue(op.getInput()),
4203 op.getDelayAttr(), op.getLengthAttr());
4206LogicalResult FIRRTLLowering::visitExpr(LTLConcatIntrinsicOp op) {
4207 return setLoweringToLTL<ltl::ConcatOp>(
4209 ValueRange{getLoweredValue(op.getLhs()), getLoweredValue(op.getRhs())});
4212LogicalResult FIRRTLLowering::visitExpr(LTLRepeatIntrinsicOp op) {
4213 return setLoweringToLTL<ltl::RepeatOp>(op, getLoweredValue(op.getInput()),
4214 op.getBaseAttr(), op.getMoreAttr());
4217LogicalResult FIRRTLLowering::visitExpr(LTLGoToRepeatIntrinsicOp op) {
4218 return setLoweringToLTL<ltl::GoToRepeatOp>(
4219 op, getLoweredValue(op.getInput()), op.getBaseAttr(), op.getMoreAttr());
4222LogicalResult FIRRTLLowering::visitExpr(LTLNonConsecutiveRepeatIntrinsicOp op) {
4223 return setLoweringToLTL<ltl::NonConsecutiveRepeatOp>(
4224 op, getLoweredValue(op.getInput()), op.getBaseAttr(), op.getMoreAttr());
4227LogicalResult FIRRTLLowering::visitExpr(LTLNotIntrinsicOp op) {
4228 return setLoweringToLTL<ltl::NotOp>(op, getLoweredValue(op.getInput()));
4231LogicalResult FIRRTLLowering::visitExpr(LTLImplicationIntrinsicOp op) {
4232 return setLoweringToLTL<ltl::ImplicationOp>(
4234 ValueRange{getLoweredValue(op.getLhs()), getLoweredValue(op.getRhs())});
4237LogicalResult FIRRTLLowering::visitExpr(LTLUntilIntrinsicOp op) {
4238 return setLoweringToLTL<ltl::UntilOp>(
4240 ValueRange{getLoweredValue(op.getLhs()), getLoweredValue(op.getRhs())});
4243LogicalResult FIRRTLLowering::visitExpr(LTLEventuallyIntrinsicOp op) {
4244 return setLoweringToLTL<ltl::EventuallyOp>(op,
4245 getLoweredValue(op.getInput()));
4248LogicalResult FIRRTLLowering::visitExpr(LTLClockIntrinsicOp op) {
4249 return setLoweringToLTL<ltl::ClockOp>(op, getLoweredValue(op.getInput()),
4250 ltl::ClockEdge::Pos,
4251 getLoweredNonClockValue(op.getClock()));
4254template <
typename TargetOp,
typename IntrinsicOp>
4255LogicalResult FIRRTLLowering::lowerVerifIntrinsicOp(IntrinsicOp op) {
4256 auto property = getLoweredValue(op.getProperty());
4257 auto enable = op.getEnable() ? getLoweredValue(op.getEnable()) : Value();
4258 TargetOp::create(builder, property, enable, op.getLabelAttr());
4262LogicalResult FIRRTLLowering::visitStmt(VerifAssertIntrinsicOp op) {
4263 return lowerVerifIntrinsicOp<verif::AssertOp>(op);
4266LogicalResult FIRRTLLowering::visitStmt(VerifAssumeIntrinsicOp op) {
4267 return lowerVerifIntrinsicOp<verif::AssumeOp>(op);
4270LogicalResult FIRRTLLowering::visitStmt(VerifCoverIntrinsicOp op) {
4271 return lowerVerifIntrinsicOp<verif::CoverOp>(op);
4274LogicalResult FIRRTLLowering::visitStmt(VerifRequireIntrinsicOp op) {
4275 if (!isa<verif::ContractOp>(op->getParentOp()))
4276 return lowerVerifIntrinsicOp<verif::AssertOp>(op);
4277 return lowerVerifIntrinsicOp<verif::RequireOp>(op);
4280LogicalResult FIRRTLLowering::visitStmt(VerifEnsureIntrinsicOp op) {
4281 if (!isa<verif::ContractOp>(op->getParentOp()))
4282 return lowerVerifIntrinsicOp<verif::AssertOp>(op);
4283 return lowerVerifIntrinsicOp<verif::EnsureOp>(op);
4286LogicalResult FIRRTLLowering::visitExpr(HasBeenResetIntrinsicOp op) {
4287 auto clock = getLoweredNonClockValue(op.getClock());
4288 auto reset = getLoweredValue(op.getReset());
4289 if (!clock || !reset)
4291 auto resetType = op.getReset().getType();
4292 auto uintResetType = dyn_cast<UIntType>(resetType);
4293 auto isSync = uintResetType && uintResetType.getWidth() == 1;
4294 auto isAsync = isa<AsyncResetType>(resetType);
4295 if (!isAsync && !isSync) {
4296 auto d = op.emitError(
"uninferred reset passed to 'has_been_reset'; "
4297 "requires sync or async reset");
4298 d.attachNote() <<
"reset is of type " << resetType
4299 <<
", should be '!firrtl.uint<1>' or '!firrtl.asyncreset'";
4302 return setLoweringTo<verif::HasBeenResetOp>(op, clock, reset, isAsync);
4309LogicalResult FIRRTLLowering::visitExpr(BitsPrimOp op) {
4310 auto input = getLoweredValue(op.getInput());
4314 Type resultType = builder.getIntegerType(op.getHi() - op.getLo() + 1);
4315 return setLoweringTo<comb::ExtractOp>(op, resultType, input, op.getLo());
4318LogicalResult FIRRTLLowering::visitExpr(InvalidValueOp op) {
4319 auto resultTy =
lowerType(op.getType());
4326 if (type_isa<AnalogType>(op.getType()))
4329 return setLoweringTo<sv::WireOp>(op, resultTy,
".invalid_analog");
4332 if (type_cast<FIRRTLBaseType>(op.getType()).containsAnalog())
4343 auto constant = getOrCreateIntConstant(*bitwidth, 0);
4345 if (!type_isa<IntegerType>(resultTy))
4347 return setLowering(op, constant);
4351 op.emitOpError(
"unsupported type");
4355LogicalResult FIRRTLLowering::visitExpr(HeadPrimOp op) {
4356 auto input = getLoweredValue(op.getInput());
4359 auto inWidth = type_cast<IntegerType>(input.getType()).getWidth();
4360 if (op.getAmount() == 0)
4361 return setLowering(op, Value());
4362 Type resultType = builder.getIntegerType(op.getAmount());
4363 return setLoweringTo<comb::ExtractOp>(op, resultType, input,
4364 inWidth - op.getAmount());
4367LogicalResult FIRRTLLowering::visitExpr(ShlPrimOp op) {
4368 auto input = getLoweredValue(op.getInput());
4371 if (op.getAmount() == 0)
4373 return setLowering(op, getOrCreateIntConstant(op.getAmount(), 0));
4378 if (op.getAmount() == 0)
4379 return setLowering(op, input);
4381 auto zero = getOrCreateIntConstant(op.getAmount(), 0);
4382 return setLoweringTo<comb::ConcatOp>(op, input, zero);
4385LogicalResult FIRRTLLowering::visitExpr(ShrPrimOp op) {
4386 auto input = getLoweredValue(op.getInput());
4391 auto inWidth = type_cast<IntegerType>(input.getType()).getWidth();
4392 auto shiftAmount = op.getAmount();
4393 if (shiftAmount >= inWidth) {
4395 if (type_cast<IntType>(op.getInput().getType()).isUnsigned())
4396 return setLowering(op, {});
4399 shiftAmount = inWidth - 1;
4402 Type resultType = builder.getIntegerType(inWidth - shiftAmount);
4403 return setLoweringTo<comb::ExtractOp>(op, resultType, input, shiftAmount);
4406LogicalResult FIRRTLLowering::visitExpr(TailPrimOp op) {
4407 auto input = getLoweredValue(op.getInput());
4411 auto inWidth = type_cast<IntegerType>(input.getType()).getWidth();
4412 if (inWidth == op.getAmount())
4413 return setLowering(op, Value());
4414 Type resultType = builder.getIntegerType(inWidth - op.getAmount());
4415 return setLoweringTo<comb::ExtractOp>(op, resultType, input, 0);
4418LogicalResult FIRRTLLowering::visitExpr(MuxPrimOp op) {
4419 auto cond = getLoweredValue(op.getSel());
4420 auto ifTrue = getLoweredAndExtendedValue(op.getHigh(), op.getType());
4421 auto ifFalse = getLoweredAndExtendedValue(op.getLow(), op.getType());
4422 if (!cond || !ifTrue || !ifFalse)
4425 if (isa<ClockType>(op.getType()))
4426 return setLoweringTo<seq::ClockMuxOp>(op, cond, ifTrue, ifFalse);
4427 return setLoweringTo<comb::MuxOp>(op, ifTrue.getType(), cond, ifTrue, ifFalse,
4431LogicalResult FIRRTLLowering::visitExpr(Mux2CellIntrinsicOp op) {
4432 auto cond = getLoweredValue(op.getSel());
4433 auto ifTrue = getLoweredAndExtendedValue(op.getHigh(), op.getType());
4434 auto ifFalse = getLoweredAndExtendedValue(op.getLow(), op.getType());
4435 if (!cond || !ifTrue || !ifFalse)
4438 auto val = comb::MuxOp::create(builder, ifTrue.getType(), cond, ifTrue,
4440 return setLowering(op, createValueWithMuxAnnotation(val,
true));
4443LogicalResult FIRRTLLowering::visitExpr(Mux4CellIntrinsicOp op) {
4444 auto sel = getLoweredValue(op.getSel());
4445 auto v3 = getLoweredAndExtendedValue(op.getV3(), op.getType());
4446 auto v2 = getLoweredAndExtendedValue(op.getV2(), op.getType());
4447 auto v1 = getLoweredAndExtendedValue(op.getV1(), op.getType());
4448 auto v0 = getLoweredAndExtendedValue(op.getV0(), op.getType());
4449 if (!sel || !v3 || !v2 || !v1 || !v0)
4451 Value array[] = {v3, v2, v1, v0};
4454 return setLowering(op, createValueWithMuxAnnotation(val,
false));
4473Value FIRRTLLowering::createValueWithMuxAnnotation(Operation *op,
bool isMux2) {
4474 assert(op->getNumResults() == 1 &&
"only expect a single result");
4475 auto val = op->getResult(0);
4479 op, sv::SVAttributeAttr::get(builder.getContext(),
"cadence map_to_mux",
4486 OpBuilder::InsertionGuard guard(builder);
4487 builder.setInsertionPoint(op);
4488 StringRef namehint = isMux2 ?
"mux2cell_in" :
"mux4cell_in";
4489 for (
auto [idx, operand] :
llvm::enumerate(op->getOperands())) {
4491 op->getContext(),
nullptr, 0,
4494 hw::WireOp::create(builder, operand, namehint + Twine(idx), innerSym);
4495 op->setOperand(idx, wire);
4500 sv::setSVAttributes(assignOp,
4501 sv::SVAttributeAttr::get(builder.getContext(),
4502 "synopsys infer_mux_override",
4507Value FIRRTLLowering::createArrayIndexing(Value array, Value index) {
4509 auto size = hw::type_cast<hw::ArrayType>(array.getType()).getNumElements();
4514 if (!llvm::isPowerOf2_64(size)) {
4515 auto extElem = getOrCreateIntConstant(APInt(llvm::Log2_64_Ceil(size), 0));
4517 SmallVector<Value> temp(llvm::NextPowerOf2(size) - size, extValue);
4519 Value temp2[] = {ext.getResult(), array};
4525 return inBoundsRead;
4528LogicalResult FIRRTLLowering::visitExpr(MultibitMuxOp op) {
4530 auto index = getLoweredAndExtOrTruncValue(
4532 UIntType::get(op.getContext(),
4537 SmallVector<Value> loweredInputs;
4538 loweredInputs.reserve(op.getInputs().size());
4539 for (
auto input : op.getInputs()) {
4540 auto lowered = getLoweredAndExtendedValue(input, op.getType());
4543 loweredInputs.push_back(lowered);
4547 return setLowering(op, createArrayIndexing(array, index));
4550LogicalResult FIRRTLLowering::visitExpr(VerbatimExprOp op) {
4551 auto resultTy =
lowerType(op.getType());
4555 SmallVector<Value, 4> operands;
4556 operands.reserve(op.getSubstitutions().size());
4557 for (
auto operand : op.getSubstitutions()) {
4558 auto lowered = getLoweredValue(operand);
4561 operands.push_back(lowered);
4564 ArrayAttr symbols = op.getSymbolsAttr();
4566 symbols = ArrayAttr::get(op.getContext(), {});
4568 return setLoweringTo<sv::VerbatimExprOp>(op, resultTy, op.getTextAttr(),
4572LogicalResult FIRRTLLowering::visitExpr(XMRRefOp op) {
4576 Type baseType = op.getType().getType();
4579 if (isa<ClockType>(baseType))
4580 xmrType = builder.getIntegerType(1);
4584 return setLoweringTo<sv::XMRRefOp>(op, sv::InOutType::get(xmrType),
4585 op.getRef(), op.getVerbatimSuffixAttr());
4588LogicalResult FIRRTLLowering::visitExpr(XMRDerefOp op) {
4592 if (isa<ClockType>(op.getType()))
4593 xmrType = builder.getIntegerType(1);
4597 auto xmr = sv::XMRRefOp::create(builder, sv::InOutType::get(xmrType),
4598 op.getRef(), op.getVerbatimSuffixAttr());
4599 auto readXmr = getReadValue(xmr);
4600 if (!isa<ClockType>(op.getType()))
4601 return setLowering(op, readXmr);
4602 return setLoweringTo<seq::ToClockOp>(op, readXmr);
4607LogicalResult FIRRTLLowering::visitExpr(TimeOp op) {
return success(); }
4608LogicalResult FIRRTLLowering::visitExpr(HierarchicalModuleNameOp op) {
4616LogicalResult FIRRTLLowering::visitStmt(SkipOp op) {
4628FailureOr<bool> FIRRTLLowering::lowerConnect(Value destVal, Value srcVal) {
4629 auto srcType = srcVal.getType();
4630 auto dstType = destVal.getType();
4631 if (srcType != dstType &&
4632 (isa<hw::TypeAliasType>(srcType) || isa<hw::TypeAliasType>(dstType))) {
4635 return TypeSwitch<Operation *, FailureOr<bool>>(destVal.getDefiningOp())
4636 .Case<hw::WireOp>([&](
auto op) {
4637 maybeUnused(op.getInput());
4638 op.getInputMutable().assign(srcVal);
4641 .Case<seq::FirRegOp>([&](
auto op) {
4642 maybeUnused(op.getNext());
4643 op.getNextMutable().assign(srcVal);
4646 .Case<hw::StructExtractOp, hw::ArrayGetOp>([](
auto op) {
4649 op.emitOpError(
"used as connect destination");
4652 .Default([](
auto) {
return false; });
4655LogicalResult FIRRTLLowering::visitStmt(ConnectOp op) {
4656 auto dest = op.getDest();
4658 auto destType = type_cast<FIRRTLBaseType>(dest.getType()).getPassiveType();
4659 auto srcVal = getLoweredAndExtendedValue(op.getSrc(), destType);
4661 return handleZeroBit(op.getSrc(), []() { return success(); });
4663 auto destVal = getPossiblyInoutLoweredValue(dest);
4667 auto result = lowerConnect(destVal, srcVal);
4675 if (updateIfBackedge(destVal, srcVal))
4678 if (!isa<hw::InOutType>(destVal.getType()))
4679 return op.emitError(
"destination isn't an inout type");
4685LogicalResult FIRRTLLowering::visitStmt(MatchingConnectOp op) {
4686 auto dest = op.getDest();
4687 auto srcVal = getLoweredValue(op.getSrc());
4689 return handleZeroBit(op.getSrc(), []() { return success(); });
4691 auto destVal = getPossiblyInoutLoweredValue(dest);
4695 auto result = lowerConnect(destVal, srcVal);
4703 if (updateIfBackedge(destVal, srcVal))
4706 if (!isa<hw::InOutType>(destVal.getType()))
4707 return op.emitError(
"destination isn't an inout type");
4713LogicalResult FIRRTLLowering::visitStmt(ForceOp op) {
4714 auto srcVal = getLoweredValue(op.getSrc());
4718 auto destVal = getPossiblyInoutLoweredValue(op.getDest());
4722 if (!isa<hw::InOutType>(destVal.getType()))
4723 return op.emitError(
"destination isn't an inout type");
4726 circuitState.addMacroDecl(builder.getStringAttr(
"SYNTHESIS"));
4727 addToIfDefBlock(
"SYNTHESIS", std::function<
void()>(), [&]() {
4728 addToInitialBlock([&]() { sv::ForceOp::create(builder, destVal, srcVal); });
4733LogicalResult FIRRTLLowering::visitStmt(RefForceOp op) {
4734 auto src = getLoweredNonClockValue(op.getSrc());
4735 auto clock = getLoweredNonClockValue(op.getClock());
4736 auto pred = getLoweredValue(op.getPredicate());
4737 if (!src || !clock || !pred)
4740 auto destVal = getPossiblyInoutLoweredValue(op.getDest());
4745 circuitState.addMacroDecl(builder.getStringAttr(
"SYNTHESIS"));
4746 addToIfDefBlock(
"SYNTHESIS", std::function<
void()>(), [&]() {
4747 addToAlwaysBlock(clock, [&]() {
4748 addIfProceduralBlock(
4749 pred, [&]() { sv::ForceOp::create(builder, destVal, src); });
4754LogicalResult FIRRTLLowering::visitStmt(RefForceInitialOp op) {
4755 auto src = getLoweredNonClockValue(op.getSrc());
4756 auto pred = getLoweredValue(op.getPredicate());
4760 auto destVal = getPossiblyInoutLoweredValue(op.getDest());
4765 circuitState.addMacroDecl(builder.getStringAttr(
"SYNTHESIS"));
4766 addToIfDefBlock(
"SYNTHESIS", std::function<
void()>(), [&]() {
4767 addToInitialBlock([&]() {
4768 addIfProceduralBlock(
4769 pred, [&]() { sv::ForceOp::create(builder, destVal, src); });
4774LogicalResult FIRRTLLowering::visitStmt(RefReleaseOp op) {
4775 auto clock = getLoweredNonClockValue(op.getClock());
4776 auto pred = getLoweredValue(op.getPredicate());
4777 if (!clock || !pred)
4780 auto destVal = getPossiblyInoutLoweredValue(op.getDest());
4785 circuitState.addMacroDecl(builder.getStringAttr(
"SYNTHESIS"));
4786 addToIfDefBlock(
"SYNTHESIS", std::function<
void()>(), [&]() {
4787 addToAlwaysBlock(clock, [&]() {
4788 addIfProceduralBlock(pred,
4789 [&]() { sv::ReleaseOp::create(builder, destVal); });
4794LogicalResult FIRRTLLowering::visitStmt(RefReleaseInitialOp op) {
4795 auto destVal = getPossiblyInoutLoweredValue(op.getDest());
4796 auto pred = getLoweredValue(op.getPredicate());
4797 if (!destVal || !pred)
4801 circuitState.addMacroDecl(builder.getStringAttr(
"SYNTHESIS"));
4802 addToIfDefBlock(
"SYNTHESIS", std::function<
void()>(), [&]() {
4803 addToInitialBlock([&]() {
4804 addIfProceduralBlock(pred,
4805 [&]() { sv::ReleaseOp::create(builder, destVal); });
4813 StringRef originalFormatString,
4814 mlir::OperandRange operands,
4815 StringAttr &result) {
4818 SmallString<32> formatString;
4819 for (
size_t i = 0, e = originalFormatString.size(), subIdx = 0; i != e; ++i) {
4820 char c = originalFormatString[i];
4824 formatString.push_back(c);
4827 SmallString<6> width;
4828 c = originalFormatString[++i];
4831 c = originalFormatString[++i];
4842 formatString.append(width);
4848 formatString.push_back(c);
4855 if (originalFormatString.slice(i, i + 4) !=
"{{}}") {
4856 formatString.push_back(c);
4860 auto substitution = operands[subIdx++];
4861 assert(type_isa<FStringType>(substitution.getType()) &&
4862 "the operand for a '{{}}' substitution must be an 'fstring' type");
4864 TypeSwitch<Operation *, LogicalResult>(substitution.getDefiningOp())
4865 .template Case<TimeOp>([&](
auto) {
4866 formatString.append(
"%0t");
4869 .
template Case<HierarchicalModuleNameOp>([&](
auto) {
4870 formatString.append(
"%m");
4873 .Default([&](
auto) {
4874 emitError(loc,
"has a substitution with an unimplemented "
4876 .attachNote(substitution.getLoc())
4877 <<
"op with an unimplemented lowering is here";
4887 formatString.push_back(c);
4891 result = StringAttr::get(loc->getContext(), formatString);
4898LogicalResult FIRRTLLowering::visitPrintfLike(
4899 T op,
const FileDescriptorInfo &fileDescriptorInfo,
bool usePrintfCond) {
4900 auto clock = getLoweredNonClockValue(op.getClock());
4901 auto cond = getLoweredValue(op.getCond());
4902 if (!clock || !cond)
4905 StringAttr formatString;
4907 op.getSubstitutions(), formatString)))
4910 auto fn = [&](Value fd) {
4911 SmallVector<Value> operands;
4912 if (failed(loweredFmtOperands(op.getSubstitutions(), operands)))
4914 sv::FWriteOp::create(builder, op.getLoc(), fd, formatString, operands);
4918 return lowerStatementWithFd(fileDescriptorInfo, clock, cond, fn,
4922LogicalResult FIRRTLLowering::visitStmt(FPrintFOp op) {
4923 StringAttr outputFileAttr;
4925 op.getOutputFileSubstitutions(),
4929 FileDescriptorInfo outputFile(outputFileAttr,
4930 op.getOutputFileSubstitutions());
4931 return visitPrintfLike(op, outputFile,
false);
4935LogicalResult FIRRTLLowering::visitStmt(FFlushOp op) {
4936 auto clock = getLoweredNonClockValue(op.getClock());
4937 auto cond = getLoweredValue(op.getCond());
4938 if (!clock || !cond)
4941 auto fn = [&](Value fd) {
4942 sv::FFlushOp::create(builder, op.getLoc(), fd);
4946 if (!op.getOutputFileAttr())
4947 return lowerStatementWithFd({}, clock, cond, fn,
false);
4951 StringAttr outputFileAttr;
4953 op.getOutputFileSubstitutions(),
4957 return lowerStatementWithFd(
4958 FileDescriptorInfo(outputFileAttr, op.getOutputFileSubstitutions()),
4959 clock, cond, fn,
false);
4964LogicalResult FIRRTLLowering::visitStmt(StopOp op) {
4965 auto clock = getLoweredValue(op.getClock());
4966 auto cond = getLoweredValue(op.getCond());
4967 if (!clock || !cond)
4970 circuitState.usedStopCond =
true;
4971 circuitState.addFragment(theModule,
"STOP_COND_FRAGMENT");
4974 sv::MacroRefExprOp::create(builder, cond.getType(),
"STOP_COND_");
4975 Value exitCond = builder.createOrFold<
comb::AndOp>(stopCond, cond,
true);
4977 sim::ClockedTerminateOp::create(builder, clock, exitCond,
4978 op.getExitCode() == 0,
4987template <
typename... Args>
4989 StringRef opName, Args &&...args) {
4990 if (opName ==
"assert")
4991 return sv::AssertOp::create(builder, std::forward<Args>(args)...);
4992 if (opName ==
"assume")
4993 return sv::AssumeOp::create(builder, std::forward<Args>(args)...);
4994 if (opName ==
"cover")
4995 return sv::CoverOp::create(builder, std::forward<Args>(args)...);
4996 llvm_unreachable(
"unknown verification op");
5002template <
typename... Args>
5004 StringRef opName, Args &&...args) {
5005 if (opName ==
"assert")
5006 return sv::AssertConcurrentOp::create(builder, std::forward<Args>(args)...);
5007 if (opName ==
"assume")
5008 return sv::AssumeConcurrentOp::create(builder, std::forward<Args>(args)...);
5009 if (opName ==
"cover")
5010 return sv::CoverConcurrentOp::create(builder, std::forward<Args>(args)...);
5011 llvm_unreachable(
"unknown verification op");
5032LogicalResult FIRRTLLowering::lowerVerificationStatement(
5033 Operation *op, StringRef labelPrefix, Value opClock, Value opPredicate,
5034 Value opEnable, StringAttr opMessageAttr, ValueRange opOperands,
5035 StringAttr opNameAttr,
bool isConcurrent, EventControl opEventControl) {
5036 StringRef opName = op->getName().stripDialect();
5039 ArrayRef<Attribute> guards{};
5040 if (
auto guardsAttr = op->template getAttrOfType<ArrayAttr>(
"guards"))
5041 guards = guardsAttr.getValue();
5043 auto isCover = isa<CoverOp>(op);
5044 auto clock = getLoweredNonClockValue(opClock);
5045 auto enable = getLoweredValue(opEnable);
5046 auto predicate = getLoweredValue(opPredicate);
5047 if (!clock || !enable || !predicate)
5051 if (opNameAttr && !opNameAttr.getValue().empty())
5053 StringAttr prefixedLabel;
5056 StringAttr::get(builder.getContext(), labelPrefix + label.getValue());
5059 SmallVector<Value> messageOps;
5063 if (flavor == VerificationFlavor::IfElseFatal && !isa<AssertOp>(op))
5064 flavor = VerificationFlavor::None;
5066 if (flavor == VerificationFlavor::None) {
5070 auto format = op->getAttrOfType<StringAttr>(
"format");
5072 if (isConcurrent && format && format.getValue() ==
"ifElseFatal") {
5073 if (!isa<AssertOp>(op))
5074 return op->emitError()
5075 <<
"ifElseFatal format cannot be used for non-assertions";
5076 flavor = VerificationFlavor::IfElseFatal;
5077 }
else if (isConcurrent)
5078 flavor = VerificationFlavor::SVA;
5080 flavor = VerificationFlavor::Immediate;
5083 if (!isCover && opMessageAttr && !opMessageAttr.getValue().empty()) {
5084 message = opMessageAttr;
5085 if (failed(loweredFmtOperands(opOperands, messageOps)))
5088 if (flavor == VerificationFlavor::SVA) {
5093 for (
auto &loweredValue : messageOps)
5094 loweredValue =
sv::SampledOp::create(builder, loweredValue);
5100 case VerificationFlavor::Immediate: {
5102 auto deferImmediate = circt::sv::DeferAssertAttr::get(
5103 builder.getContext(), circt::sv::DeferAssert::Immediate);
5104 addToAlwaysBlock(clock, [&]() {
5105 addIfProceduralBlock(enable, [&]() {
5107 prefixedLabel, message, messageOps);
5112 case VerificationFlavor::IfElseFatal: {
5113 assert(isa<AssertOp>(op) &&
"only assert is expected");
5116 auto boolType = IntegerType::get(builder.getContext(), 1);
5117 predicate = comb::createOrFoldNot(predicate, builder,
true);
5118 predicate = builder.createOrFold<
comb::AndOp>(enable, predicate,
true);
5120 circuitState.addMacroDecl(builder.getStringAttr(
"SYNTHESIS"));
5121 addToIfDefBlock(
"SYNTHESIS", {}, [&]() {
5122 addToAlwaysBlock(clock, [&]() {
5123 addIfProceduralBlock(predicate, [&]() {
5124 circuitState.usedStopCond =
true;
5125 circuitState.addFragment(theModule,
"STOP_COND_FRAGMENT");
5127 circuitState.usedAssertVerboseCond =
true;
5128 circuitState.addFragment(theModule,
"ASSERT_VERBOSE_COND_FRAGMENT");
5130 addIfProceduralBlock(
5131 sv::MacroRefExprOp::create(builder, boolType,
5132 "ASSERT_VERBOSE_COND_"),
5133 [&]() { sv::ErrorOp::create(builder, message, messageOps); });
5134 addIfProceduralBlock(
5135 sv::MacroRefExprOp::create(builder, boolType,
"STOP_COND_"),
5136 [&]() { sv::FatalOp::create(builder); });
5142 case VerificationFlavor::SVA: {
5147 comb::createOrFoldNot(enable, builder,
true);
5149 builder.createOrFold<
comb::OrOp>(notEnable, predicate,
true);
5151 predicate = builder.createOrFold<
comb::AndOp>(enable, predicate,
true);
5155 sv::EventControl event;
5156 switch (opEventControl) {
5157 case EventControl::AtPosEdge:
5158 event = circt::sv::EventControl::AtPosEdge;
5160 case EventControl::AtEdge:
5161 event = circt::sv::EventControl::AtEdge;
5163 case EventControl::AtNegEdge:
5164 event = circt::sv::EventControl::AtNegEdge;
5170 circt::sv::EventControlAttr::get(builder.getContext(), event), clock,
5171 predicate, prefixedLabel, message, messageOps);
5174 case VerificationFlavor::None:
5176 "flavor `None` must be converted into one of concreate flavors");
5183 return emitGuards(op->getLoc(), guards,
emit);
5187LogicalResult FIRRTLLowering::visitStmt(AssertOp op) {
5188 return lowerVerificationStatement(
5189 op,
"assert__", op.getClock(), op.getPredicate(), op.getEnable(),
5190 op.getMessageAttr(), op.getSubstitutions(), op.getNameAttr(),
5191 op.getIsConcurrent(), op.getEventControl());
5195LogicalResult FIRRTLLowering::visitStmt(AssumeOp op) {
5196 return lowerVerificationStatement(
5197 op,
"assume__", op.getClock(), op.getPredicate(), op.getEnable(),
5198 op.getMessageAttr(), op.getSubstitutions(), op.getNameAttr(),
5199 op.getIsConcurrent(), op.getEventControl());
5203LogicalResult FIRRTLLowering::visitStmt(CoverOp op) {
5204 return lowerVerificationStatement(
5205 op,
"cover__", op.getClock(), op.getPredicate(), op.getEnable(),
5206 op.getMessageAttr(), op.getSubstitutions(), op.getNameAttr(),
5207 op.getIsConcurrent(), op.getEventControl());
5211LogicalResult FIRRTLLowering::visitStmt(UnclockedAssumeIntrinsicOp op) {
5216 auto guardsAttr = op->getAttrOfType<mlir::ArrayAttr>(
"guards");
5217 ArrayRef<Attribute> guards =
5218 guardsAttr ? guardsAttr.getValue() : ArrayRef<Attribute>();
5220 auto label = op.getNameAttr();
5221 StringAttr assumeLabel;
5222 if (label && !label.empty())
5224 StringAttr::get(builder.getContext(),
"assume__" + label.getValue());
5225 auto predicate = getLoweredValue(op.getPredicate());
5226 auto enable = getLoweredValue(op.getEnable());
5227 auto notEnable = comb::createOrFoldNot(enable, builder,
true);
5228 predicate = builder.createOrFold<
comb::OrOp>(notEnable, predicate,
true);
5230 SmallVector<Value> messageOps;
5231 for (
auto operand : op.getSubstitutions()) {
5232 auto loweredValue = getLoweredValue(operand);
5233 if (!loweredValue) {
5237 loweredValue = getOrCreateIntConstant(1, 0);
5239 messageOps.push_back(loweredValue);
5241 return emitGuards(op.getLoc(), guards, [&]() {
5242 sv::AlwaysOp::create(
5243 builder, ArrayRef(sv::EventControl::AtEdge), ArrayRef(predicate),
5245 if (op.getMessageAttr().getValue().empty())
5246 buildImmediateVerifOp(
5247 builder,
"assume", predicate,
5248 circt::sv::DeferAssertAttr::get(
5249 builder.getContext(), circt::sv::DeferAssert::Immediate),
5252 buildImmediateVerifOp(
5253 builder,
"assume", predicate,
5254 circt::sv::DeferAssertAttr::get(
5255 builder.getContext(), circt::sv::DeferAssert::Immediate),
5256 assumeLabel, op.getMessageAttr(), messageOps);
5261LogicalResult FIRRTLLowering::visitStmt(AttachOp op) {
5263 if (op.getAttached().size() < 2)
5266 SmallVector<Value, 4> inoutValues;
5267 for (
auto v : op.getAttached()) {
5268 inoutValues.push_back(getPossiblyInoutLoweredValue(v));
5269 if (!inoutValues.back()) {
5273 inoutValues.pop_back();
5277 if (!isa<hw::InOutType>(inoutValues.back().getType()))
5278 return op.emitError(
"operand isn't an inout type");
5281 if (inoutValues.size() < 2)
5292 bool isAttachInternalOnly =
5293 llvm::none_of(inoutValues, [](
auto v) {
return isa<BlockArgument>(v); });
5295 if (isAttachInternalOnly) {
5296 auto v0 = inoutValues.front();
5297 for (
auto v : inoutValues) {
5300 v.replaceAllUsesWith(v0);
5307 circuitState.addMacroDecl(builder.getStringAttr(
"SYNTHESIS"));
5308 circuitState.addMacroDecl(builder.getStringAttr(
"VERILATOR"));
5313 SmallVector<Value, 4> values;
5314 for (
auto inoutValue : inoutValues)
5315 values.push_back(getReadValue(inoutValue));
5317 for (
size_t i1 = 0, e = inoutValues.size(); i1 != e; ++i1) {
5318 for (
size_t i2 = 0; i2 != e; ++i2)
5326 sv::IfDefOp::create(
5327 builder,
"VERILATOR",
5329 sv::VerbatimOp::create(
5331 "`error \"Verilator does not support alias and thus "
5333 "arbitrarily connect bidirectional wires and ports\"");
5335 [&]() { sv::AliasOp::create(builder, inoutValues); });
5341LogicalResult FIRRTLLowering::visitStmt(BindOp op) {
5342 sv::BindOp::create(builder, op.getInstanceAttr());
5346LogicalResult FIRRTLLowering::fixupLTLOps() {
5347 if (ltlOpFixupWorklist.empty())
5349 LLVM_DEBUG(llvm::dbgs() <<
"Fixing up " << ltlOpFixupWorklist.size()
5353 for (
unsigned i = 0, e = ltlOpFixupWorklist.size(); i != e; ++i)
5354 for (
auto *user : ltlOpFixupWorklist[i]->getUsers())
5355 if (isa<
hw::WireOp>(user))
5356 ltlOpFixupWorklist.insert(user);
5359 while (!ltlOpFixupWorklist.empty()) {
5360 auto *op = ltlOpFixupWorklist.pop_back_val();
5363 if (
auto opIntf = dyn_cast_or_null<mlir::InferTypeOpInterface>(op)) {
5364 LLVM_DEBUG(llvm::dbgs() <<
"- Update " << *op <<
"\n");
5365 SmallVector<Type, 2> types;
5366 auto result = opIntf.inferReturnTypes(
5367 op->getContext(), op->getLoc(), op->getOperands(),
5368 op->getAttrDictionary(), op->getPropertiesStorage(), op->getRegions(),
5372 assert(types.size() == op->getNumResults());
5376 for (
auto [result, type] :
llvm::zip(op->getResults(), types)) {
5377 if (result.getType() == type)
5379 LLVM_DEBUG(llvm::dbgs()
5380 <<
" - Result #" << result.getResultNumber() <<
" from "
5381 << result.getType() <<
" to " << type <<
"\n");
5382 result.setType(type);
5383 for (
auto *user : result.getUsers())
5385 ltlOpFixupWorklist.insert(user);
5390 if (
auto wireOp = dyn_cast<hw::WireOp>(op)) {
5391 if (isa<ltl::SequenceType, ltl::PropertyType>(wireOp.getType())) {
5392 wireOp.replaceAllUsesWith(wireOp.getInput());
5393 LLVM_DEBUG(llvm::dbgs() <<
"- Remove " << wireOp <<
"\n");
5394 if (wireOp.use_empty())
5401 SmallPtrSet<Operation *, 4> usersReported;
5402 for (
auto *user : op->getUsers()) {
5403 if (!usersReported.insert(user).second)
5405 if (isa_and_nonnull<ltl::LTLDialect, verif::VerifDialect>(
5406 user->getDialect()))
5408 if (isa<hw::WireOp>(user))
5410 auto d = op->emitError(
5411 "verification operation used in a non-verification context");
5412 d.attachNote(user->getLoc())
5413 <<
"leaking outside verification context here";
assert(baseType &&"element must be base type")
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, mlir::OperandRange 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(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.
constexpr const char * extractCoverageAnnoClass
constexpr const char * elaborationArtefactsDirectoryAnnoClass
FIRRTLBaseType getBaseType(Type type)
If it is a base type, return it as is.
constexpr const char * extractGrandCentralClass
constexpr const char * blackBoxAnnoClass
constexpr const char * dontObfuscateModuleAnnoClass
constexpr const char * metadataDirectoryAttrName
constexpr const char * testBenchDirAnnoClass
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.
constexpr const char * dutAnnoClass
constexpr const char * forceNameAnnoClass
constexpr const char * noDedupAnnoClass
bool hasDroppableName(Operation *op)
Return true if the name is droppable.
constexpr const char * extractAssertAnnoClass
constexpr const char * verifBlackBoxAnnoClass
constexpr const char * blackBoxTargetDirAnnoClass
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.
constexpr const char * extractAssumeAnnoClass
constexpr const char * testHarnessHierAnnoClass
constexpr const char * moduleHierAnnoClass
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)
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