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 = builder.create<mlir::UnrealizedConversionCastOp>(type, val).getResult(
144 ImplicitLocOpBuilder &builder) {
146 if (hw::StructType structTy = dyn_cast<hw::StructType>(type)) {
150 .create<mlir::UnrealizedConversionCastOp>(
151 type_cast<FIRRTLBaseType>(val.getType()).getPassiveType(), val)
153 val = builder.createOrFold<HWStructCastOp>(type, val);
158 builder.create<mlir::UnrealizedConversionCastOp>(type, val).getResult(0);
165 StringRef annoClass, StringRef attrBase) {
167 auto *ctx = top.getContext();
170 if (
auto dir = anno.getMember<StringAttr>(
"directory")) {
171 SmallVector<NamedAttribute> old;
172 for (
auto i : top->getAttrs())
175 StringAttr::get(ctx, attrBase),
176 hw::OutputFileAttr::getAsDirectory(ctx, dir.getValue(),
true,
true));
179 if (
auto file = anno.getMember<StringAttr>(
"filename")) {
180 SmallVector<NamedAttribute> old;
181 for (
auto i : top->getAttrs())
183 old.emplace_back(StringAttr::get(ctx, attrBase +
".bindfile"),
184 hw::OutputFileAttr::getFromFilename(
185 ctx, file.getValue(),
true));
191 return size == 1 ? 1 : llvm::Log2_64_Ceil(size);
197 if (
auto attr = src->getAttrOfType<StringAttr>(
"name"))
198 if (!dst->hasAttr(
"sv.namehint") && !dst->hasAttr(
"name"))
199 dst->setAttr(
"sv.namehint", attr);
205class FileDescriptorInfo {
207 FileDescriptorInfo(StringAttr outputFileName, mlir::ValueRange substitutions)
208 : outputFileFormat(outputFileName), substitutions(substitutions) {
210 substitutions.empty() &&
211 "substitutions must be empty when output file name is empty");
214 FileDescriptorInfo() =
default;
217 bool isSubstitutionRequired()
const {
return !substitutions.empty(); }
220 bool isDefaultFd()
const {
return !outputFileFormat; }
222 StringAttr getOutputFileFormat()
const {
return outputFileFormat; }
223 mlir::ValueRange getSubstitutions()
const {
return substitutions; }
227 StringAttr outputFileFormat = {};
230 mlir::ValueRange substitutions;
240struct FIRRTLModuleLowering;
243struct CircuitLoweringState {
245 std::atomic<bool> usedPrintf{
false};
246 std::atomic<bool> usedAssertVerboseCond{
false};
247 std::atomic<bool> usedStopCond{
false};
248 std::atomic<bool> usedFileDescriptorLib{
false};
250 CircuitLoweringState(CircuitOp circuitOp,
bool enableAnnotationWarning,
253 : circuitOp(circuitOp), instanceGraph(instanceGraph),
254 enableAnnotationWarning(enableAnnotationWarning),
255 verificationFlavor(verificationFlavor), nlaTable(nlaTable) {
256 auto *context = circuitOp.getContext();
261 auto dirName = tbAnno.getMember<StringAttr>(
"dirname");
262 testBenchDirectory = hw::OutputFileAttr::getAsDirectory(
263 context, dirName.getValue(),
false,
true);
267 if (
auto module = dyn_cast<FModuleLike>(op))
278 testHarness =
nullptr;
279 }
else if (dut == testHarness) {
280 testHarness =
nullptr;
285 auto inDUT = [&](igraph::ModuleOpInterface child) {
287 if (
auto inst = instRec->getInstance<InstanceOp>())
288 return inst.getLowerToBind() || inst.getDoNotPrint();
291 if (
auto parent = dyn_cast<igraph::ModuleOpInterface>(*dut))
292 return getInstanceGraph().isAncestor(child, parent, isPhony);
295 circuitOp->walk([&](FModuleLike moduleOp) {
297 dutModules.insert(moduleOp);
301 Operation *getNewModule(Operation *oldModule) {
302 auto it = oldToNewModuleMap.find(oldModule);
303 return it != oldToNewModuleMap.end() ? it->second :
nullptr;
306 Operation *getOldModule(Operation *newModule) {
307 auto it = newToOldModuleMap.find(newModule);
308 return it != newToOldModuleMap.end() ? it->second :
nullptr;
311 void recordModuleMapping(Operation *oldFMod, Operation *newHWMod) {
312 oldToNewModuleMap[oldFMod] = newHWMod;
313 newToOldModuleMap[newHWMod] = oldFMod;
318 void processRemainingAnnotations(Operation *op,
const AnnotationSet &annoSet);
324 void addBind(sv::BindOp op) {
325 std::lock_guard<std::mutex> lock(bindsMutex);
331 hw::TypeAliasType getTypeAlias(Type rawType, BaseTypeAliasType firAliasType,
334 auto hwAlias = typeAliases.getTypedecl(firAliasType);
337 assert(!typeAliases.isFrozen() &&
338 "type aliases cannot be generated after its frozen");
339 return typeAliases.addTypedecl(rawType, firAliasType, typeLoc);
342 FModuleLike getDut() {
return dut; }
343 FModuleLike getTestHarness() {
return testHarness; }
349 bool isInDUT(igraph::ModuleOpInterface child) {
350 if (
auto hwModule = dyn_cast<hw::HWModuleOp>(child.getOperation()))
351 child = cast<igraph::ModuleOpInterface>(getOldModule(hwModule));
352 return dutModules.contains(child);
355 hw::OutputFileAttr getTestBenchDirectory() {
return testBenchDirectory; }
360 bool isInTestHarness(igraph::ModuleOpInterface mod) {
return !isInDUT(mod); }
367 Type
lowerType(Type type, Location loc) {
368 return ::lowerType(type, loc,
369 [&](Type rawType, BaseTypeAliasType firrtlType,
370 Location typeLoc) -> hw::TypeAliasType {
371 return getTypeAlias(rawType, firrtlType, typeLoc);
376 friend struct FIRRTLModuleLowering;
377 friend struct FIRRTLLowering;
378 CircuitLoweringState(
const CircuitLoweringState &) =
delete;
379 void operator=(
const CircuitLoweringState &) =
delete;
382 DenseMap<Operation *, Operation *> oldToNewModuleMap;
385 DenseMap<Operation *, Operation *> newToOldModuleMap;
396 DenseSet<igraph::ModuleOpInterface> dutModules;
400 StringSet<> pendingAnnotations;
401 const bool enableAnnotationWarning;
402 std::mutex annotationPrintingMtx;
408 SmallVector<sv::BindOp> binds;
411 std::mutex bindsMutex;
419 FModuleLike testHarness;
422 hw::OutputFileAttr testBenchDirectory;
426 DenseMap<std::pair<Attribute, Attribute>, Attribute> instanceForceNames;
429 SetVector<StringAttr> macroDeclNames;
430 std::mutex macroDeclMutex;
432 void addMacroDecl(StringAttr name) {
433 std::unique_lock<std::mutex> lock(macroDeclMutex);
434 macroDeclNames.insert(name);
439 DenseMap<hw::HWModuleOp, SetVector<Attribute>> fragments;
440 llvm::sys::SmartMutex<true> fragmentsMutex;
443 llvm::sys::SmartScopedLock<true> lock(fragmentsMutex);
444 fragments[module].insert(
445 FlatSymbolRefAttr::get(circuitOp.getContext(), fragment));
460 struct RecordTypeAlias {
462 RecordTypeAlias(CircuitOp c) : circuitOp(c) {}
464 hw::TypeAliasType getTypedecl(BaseTypeAliasType firAlias)
const {
465 auto iter = firrtlTypeToAliasTypeMap.find(firAlias);
466 if (iter != firrtlTypeToAliasTypeMap.end())
471 bool isFrozen() {
return frozen; }
473 void freeze() { frozen =
true; }
475 hw::TypeAliasType addTypedecl(Type rawType, BaseTypeAliasType firAlias,
477 assert(!frozen &&
"Record already frozen, cannot be updated");
480 auto b = ImplicitLocOpBuilder::atBlockBegin(
482 &circuitOp->getParentRegion()->getBlocks().back());
484 b.getStringAttr(circuitOp.getName() +
"__TYPESCOPE_"));
485 typeScope.getBodyRegion().push_back(
new Block());
487 auto typeName = firAlias.getName();
492 StringAttr::get(typeName.getContext(),
493 typeDeclNamespace.newName(typeName.getValue()));
495 auto typeScopeBuilder =
496 ImplicitLocOpBuilder::atBlockEnd(typeLoc, typeScope.getBodyBlock());
497 auto typeDecl = typeScopeBuilder.create<
hw::TypedeclOp>(typeLoc, typeName,
499 auto hwAlias = hw::TypeAliasType::get(
500 SymbolRefAttr::get(typeScope.getSymNameAttr(),
501 {FlatSymbolRefAttr::get(typeDecl)}),
503 auto insert = firrtlTypeToAliasTypeMap.try_emplace(firAlias, hwAlias);
504 assert(insert.second &&
"Entry already exists, insert failed");
505 return insert.first->second;
514 DenseMap<Type, hw::TypeAliasType> firrtlTypeToAliasTypeMap;
522 RecordTypeAlias typeAliases = RecordTypeAlias(circuitOp);
525void CircuitLoweringState::processRemainingAnnotations(
527 if (!enableAnnotationWarning || annoSet.
empty())
529 std::lock_guard<std::mutex> lock(annotationPrintingMtx);
531 for (
auto a : annoSet) {
532 auto inserted = pendingAnnotations.insert(a.getClass());
533 if (!inserted.second)
572 mlir::emitWarning(op->getLoc(),
"unprocessed annotation:'" + a.getClass() +
573 "' still remaining after LowerToHW");
579struct FIRRTLModuleLowering
580 :
public circt::impl::LowerFIRRTLToHWBase<FIRRTLModuleLowering> {
582 void runOnOperation()
override;
583 void setEnableAnnotationWarning() { enableAnnotationWarning =
true; }
585 using LowerFIRRTLToHWBase<FIRRTLModuleLowering>::verificationFlavor;
588 void lowerFileHeader(CircuitOp op, CircuitLoweringState &
loweringState);
589 LogicalResult lowerPorts(ArrayRef<PortInfo> firrtlPorts,
590 SmallVectorImpl<hw::PortInfo> &ports,
591 Operation *moduleOp, StringRef moduleName,
593 bool handleForceNameAnnos(FModuleLike oldModule,
AnnotationSet &annos,
595 hw::HWModuleOp lowerModule(FModuleOp oldModule, Block *topLevelModule,
598 Block *topLevelModule,
601 Block *topLevelModule,
605 lowerModulePortsAndMoveBody(FModuleOp oldModule,
hw::HWModuleOp newModule,
609 LogicalResult lowerFormalBody(verif::FormalOp formalOp,
611 LogicalResult lowerSimulationBody(verif::SimulationOp simulationOp,
613 LogicalResult lowerFileBody(emit::FileOp op);
621 bool enableAnnotationWarning,
623 auto pass = std::make_unique<FIRRTLModuleLowering>();
624 if (enableAnnotationWarning)
625 pass->setEnableAnnotationWarning();
626 pass->verificationFlavor = verificationFlavor;
632void FIRRTLModuleLowering::runOnOperation() {
636 auto *topLevelModule = getOperation().getBody();
640 for (
auto &op : *topLevelModule) {
641 if ((circuit = dyn_cast<CircuitOp>(&op)))
648 auto *circuitBody = circuit.getBodyBlock();
652 CircuitLoweringState state(circuit, enableAnnotationWarning,
653 verificationFlavor, getAnalysis<InstanceGraph>(),
654 &getAnalysis<NLATable>());
656 SmallVector<Operation *, 32> opsToProcess;
660 "firrtl.extract.assert");
662 "firrtl.extract.assume");
664 "firrtl.extract.cover");
665 circuitAnno.removeAnnotationsWithClass(
668 state.processRemainingAnnotations(circuit, circuitAnno);
671 for (
auto &op : make_early_inc_range(circuitBody->getOperations())) {
673 TypeSwitch<Operation *, LogicalResult>(&op)
674 .Case<FModuleOp>([&](
auto module) {
675 auto loweredMod = lowerModule(module, topLevelModule, state);
679 state.recordModuleMapping(&op, loweredMod);
680 opsToProcess.push_back(loweredMod);
682 module.walk([&](Operation *op) {
683 for (auto res : op->getResults()) {
685 type_dyn_cast<BaseTypeAliasType>(res.getType()))
686 state.lowerType(aliasType, op->getLoc());
689 return lowerModulePortsAndMoveBody(module, loweredMod, state);
691 .Case<FExtModuleOp>([&](
auto extModule) {
693 lowerExtModule(extModule, topLevelModule, state);
696 state.recordModuleMapping(&op, loweredMod);
699 .Case<FMemModuleOp>([&](
auto memModule) {
701 lowerMemModule(memModule, topLevelModule, state);
704 state.recordModuleMapping(&op, loweredMod);
707 .Case<FormalOp>([&](
auto oldOp) {
708 auto builder = OpBuilder::atBlockEnd(topLevelModule);
709 auto newOp = builder.create<verif::FormalOp>(
710 oldOp.getLoc(), oldOp.getNameAttr(),
711 oldOp.getParametersAttr());
712 newOp.getBody().emplaceBlock();
713 state.recordModuleMapping(oldOp, newOp);
714 opsToProcess.push_back(newOp);
717 .Case<SimulationOp>([&](
auto oldOp) {
718 auto loc = oldOp.getLoc();
719 auto builder = OpBuilder::atBlockEnd(topLevelModule);
720 auto newOp = builder.create<verif::SimulationOp>(
721 loc, oldOp.getNameAttr(), oldOp.getParametersAttr());
722 auto &body = newOp.getRegion().emplaceBlock();
723 body.addArgument(seq::ClockType::get(builder.getContext()), loc);
724 body.addArgument(builder.getI1Type(), loc);
725 state.recordModuleMapping(oldOp, newOp);
726 opsToProcess.push_back(newOp);
729 .Case<emit::FileOp>([&](
auto fileOp) {
730 fileOp->moveBefore(topLevelModule, topLevelModule->end());
731 opsToProcess.push_back(fileOp);
734 .Default([&](Operation *op) {
739 op->moveBefore(topLevelModule, topLevelModule->end());
745 return signalPassFailure();
748 state.typeAliases.freeze();
753 SmallVector<Attribute> dutHierarchyFiles;
754 SmallVector<Attribute> testHarnessHierarchyFiles;
755 circuitAnno.removeAnnotations([&](
Annotation annotation) {
757 auto file = hw::OutputFileAttr::getFromFilename(
759 annotation.
getMember<StringAttr>(
"filename").getValue(),
761 dutHierarchyFiles.push_back(file);
765 auto file = hw::OutputFileAttr::getFromFilename(
767 annotation.
getMember<StringAttr>(
"filename").getValue(),
771 if (state.getTestHarness())
772 testHarnessHierarchyFiles.push_back(file);
774 dutHierarchyFiles.push_back(file);
780 if (!dutHierarchyFiles.empty())
781 state.getNewModule(state.getDut())
783 ArrayAttr::get(&getContext(), dutHierarchyFiles));
784 if (!testHarnessHierarchyFiles.empty())
785 state.getNewModule(state.getTestHarness())
787 ArrayAttr::get(&getContext(), testHarnessHierarchyFiles));
791 mlir::failableParallelForEach(&getContext(), opsToProcess, [&](
auto op) {
795 return signalPassFailure();
798 for (
auto bind : state.binds) {
803 for (
auto &[module, fragments] : state.fragments)
805 ArrayAttr::
get(&getContext(), fragments.getArrayRef()));
808 for (
auto oldNew : state.oldToNewModuleMap)
809 oldNew.first->erase();
811 if (!state.macroDeclNames.empty()) {
812 ImplicitLocOpBuilder b(UnknownLoc::get(&getContext()), circuit);
813 for (
auto name : state.macroDeclNames) {
814 b.create<sv::MacroDeclOp>(name);
819 lowerFileHeader(circuit, state);
826void FIRRTLModuleLowering::lowerFileHeader(CircuitOp op,
827 CircuitLoweringState &state) {
830 ImplicitLocOpBuilder b(UnknownLoc::get(&getContext()), op);
834 auto emitGuardedDefine = [&](StringRef guard, StringRef defName,
835 StringRef defineTrue =
"",
836 StringRef defineFalse = StringRef()) {
837 if (!defineFalse.data()) {
838 assert(defineTrue.data() &&
"didn't define anything");
840 guard, [&]() { b.create<sv::MacroDefOp>(defName, defineTrue); });
845 if (defineTrue.data())
846 b.create<sv::MacroDefOp>(defName, defineTrue);
848 [&]() { b.create<sv::MacroDefOp>(defName, defineFalse); });
853 auto emitGuard = [&](
const char *guard, llvm::function_ref<void(
void)> body) {
855 guard, []() {}, body);
858 if (state.usedFileDescriptorLib) {
860 SmallVector<hw::ModulePort> ports;
864 namePort.
name = b.getStringAttr(
"name");
865 namePort.
type = hw::StringType::get(b.getContext());
866 namePort.
dir = hw::ModulePort::Direction::Input;
867 ports.push_back(namePort);
871 fdPort.
name = b.getStringAttr(
"fd");
872 fdPort.
type = b.getIntegerType(32);
873 fdPort.
dir = hw::ModulePort::Direction::Output;
874 ports.push_back(fdPort);
877 auto moduleType = hw::ModuleType::get(b.getContext(), ports);
879 SmallVector<NamedAttribute> perArgumentsAttr;
880 perArgumentsAttr.push_back(
881 {sv::FuncOp::getExplicitlyReturnedAttrName(), b.getUnitAttr()});
883 SmallVector<Attribute> argumentAttr = {
884 DictionaryAttr::get(b.getContext(), {}),
885 DictionaryAttr::get(b.getContext(), perArgumentsAttr)};
888 auto func = b.create<sv::FuncOp>(
890 "__circt_lib_logging::FileDescriptor::get", moduleType,
893 {b.getDictionaryAttr({}), b.getDictionaryAttr(perArgumentsAttr)}),
899 b.getStringAttr(
"__circt_lib_logging::FileDescriptor::get"));
902 b.create<sv::MacroDeclOp>(
"__CIRCT_LIB_LOGGING");
904 b.create<emit::FragmentOp>(
"CIRCT_LIB_LOGGING_FRAGMENT", [&] {
905 emitGuard(
"__CIRCT_LIB_LOGGING", [&]() {
906 b.create<sv::VerbatimOp>(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 b.create<sv::MacroDefOp>("__CIRCT_LIB_LOGGING",
"");
927 if (state.usedPrintf) {
928 b.create<sv::MacroDeclOp>(
"PRINTF_COND");
929 b.create<sv::MacroDeclOp>(
"PRINTF_COND_");
930 b.create<emit::FragmentOp>(
"PRINTF_COND_FRAGMENT", [&] {
931 b.create<sv::VerbatimOp>(
932 "\n// Users can define 'PRINTF_COND' to add an extra gate to "
934 emitGuard(
"PRINTF_COND_", [&]() {
935 emitGuardedDefine(
"PRINTF_COND",
"PRINTF_COND_",
"(`PRINTF_COND)",
"1");
940 if (state.usedAssertVerboseCond) {
941 b.create<sv::MacroDeclOp>(
"ASSERT_VERBOSE_COND");
942 b.create<sv::MacroDeclOp>(
"ASSERT_VERBOSE_COND_");
943 b.create<emit::FragmentOp>(
"ASSERT_VERBOSE_COND_FRAGMENT", [&] {
944 b.create<sv::VerbatimOp>(
945 "\n// Users can define 'ASSERT_VERBOSE_COND' to add an extra "
946 "gate to assert error printing.");
947 emitGuard(
"ASSERT_VERBOSE_COND_", [&]() {
948 emitGuardedDefine(
"ASSERT_VERBOSE_COND",
"ASSERT_VERBOSE_COND_",
949 "(`ASSERT_VERBOSE_COND)",
"1");
954 if (state.usedStopCond) {
955 b.create<sv::MacroDeclOp>(
"STOP_COND");
956 b.create<sv::MacroDeclOp>(
"STOP_COND_");
957 b.create<emit::FragmentOp>(
"STOP_COND_FRAGMENT", [&] {
958 b.create<sv::VerbatimOp>(
959 "\n// Users can define 'STOP_COND' to add an extra gate "
960 "to stop conditions.");
961 emitGuard(
"STOP_COND_", [&]() {
962 emitGuardedDefine(
"STOP_COND",
"STOP_COND_",
"(`STOP_COND)",
"1");
969FIRRTLModuleLowering::lowerPorts(ArrayRef<PortInfo> firrtlPorts,
970 SmallVectorImpl<hw::PortInfo> &ports,
971 Operation *moduleOp, StringRef moduleName,
973 ports.reserve(firrtlPorts.size());
975 size_t numResults = 0;
976 for (
auto e :
llvm::enumerate(firrtlPorts)) {
978 size_t portNo = e.index();
983 if (firrtlPort.
sym.size() > 1 ||
984 (firrtlPort.
sym.size() == 1 && !firrtlPort.
sym.getSymName()))
985 return emitError(firrtlPort.
loc)
986 <<
"cannot lower aggregate port " << firrtlPort.
name
987 <<
" with field sensitive symbols, HW dialect does not support "
988 "per field symbols yet.";
989 hwPort.
setSym(firrtlPort.
sym, moduleOp->getContext());
991 if (hadDontTouch && !hwPort.
getSym()) {
992 if (hwPort.
type.isInteger(0)) {
993 if (enableAnnotationWarning) {
994 mlir::emitWarning(firrtlPort.
loc)
995 <<
"zero width port " << hwPort.
name
996 <<
" has dontTouch annotation, removing anyway";
1002 hw::InnerSymAttr::get(StringAttr::get(
1003 moduleOp->getContext(),
1004 Twine(
"__") + moduleName + Twine(
"__DONTTOUCH__") +
1005 Twine(portNo) + Twine(
"__") + firrtlPort.
name.strref())),
1006 moduleOp->getContext());
1011 moduleOp->emitError(
"cannot lower this port type to HW");
1017 if (hwPort.
type.isInteger(0)) {
1018 auto sym = hwPort.
getSym();
1019 if (sym && !sym.empty()) {
1020 return mlir::emitError(firrtlPort.
loc)
1021 <<
"zero width port " << hwPort.
name
1022 <<
" is referenced by name [" << sym
1023 <<
"] (e.g. in an XMR) but must be removed";
1030 hwPort.
dir = hw::ModulePort::Direction::Output;
1031 hwPort.
argNum = numResults++;
1032 }
else if (firrtlPort.
isInput()) {
1033 hwPort.
dir = hw::ModulePort::Direction::Input;
1034 hwPort.
argNum = numArgs++;
1038 hwPort.
type = hw::InOutType::get(hwPort.
type);
1039 hwPort.
dir = hw::ModulePort::Direction::InOut;
1040 hwPort.
argNum = numArgs++;
1042 hwPort.
loc = firrtlPort.
loc;
1043 ports.push_back(hwPort);
1053 auto params = llvm::map_range(module.getParameters(), [](Attribute a) {
1054 return cast<ParamDeclAttr>(a);
1059 Builder builder(module);
1064 SmallVector<Attribute> newParams;
1065 for (
const ParamDeclAttr &entry : params) {
1066 auto name = entry.getName();
1067 auto type = entry.getType();
1068 auto value = ignoreValues ? Attribute() : entry.getValue();
1070 hw::ParamDeclAttr::get(builder.getContext(), name, type, value);
1071 newParams.push_back(paramAttr);
1073 return builder.getArrayAttr(newParams);
1076bool FIRRTLModuleLowering::handleForceNameAnnos(
1079 bool failed =
false;
1085 auto sym = anno.
getMember<FlatSymbolRefAttr>(
"circt.nonlocal");
1092 auto diag = oldModule.emitOpError()
1094 <<
"' that is not a non-local annotation";
1095 diag.attachNote() <<
"the erroneous annotation is '" << anno.
getDict()
1106 auto diag = oldModule.emitOpError()
1108 <<
"' whose non-local symbol, '" << sym
1109 <<
"' does not exist in the circuit";
1110 diag.attachNote() <<
"the erroneous annotation is '" << anno.
getDict();
1123 cast<hw::InnerRefAttr>(nla.getNamepath().getValue().take_back(2)[0]);
1125 {{inst.getModule(), inst.getName()}, anno.
getMember(
"name")});
1126 if (!inserted.second &&
1127 (anno.
getMember(
"name") != (inserted.first->second))) {
1128 auto diag = oldModule.emitError()
1130 <<
"' with different names: " << inserted.first->second
1131 <<
" was not " << anno.
getMember(
"name");
1132 diag.attachNote() <<
"the erroneous annotation is '" << anno.
getDict()
1143FIRRTLModuleLowering::lowerExtModule(FExtModuleOp oldModule,
1144 Block *topLevelModule,
1147 SmallVector<PortInfo> firrtlPorts = oldModule.getPorts();
1148 SmallVector<hw::PortInfo, 8> ports;
1149 if (failed(lowerPorts(firrtlPorts, ports, oldModule, oldModule.getName(),
1153 StringRef verilogName;
1154 if (
auto defName = oldModule.getDefname())
1155 verilogName = defName.value();
1158 auto builder = OpBuilder::atBlockEnd(topLevelModule);
1159 auto nameAttr = builder.getStringAttr(oldModule.getName());
1165 oldModule.getLoc(), nameAttr, ports, verilogName, parameters);
1166 SymbolTable::setSymbolVisibility(newModule,
1167 SymbolTable::getSymbolVisibility(oldModule));
1169 bool hasOutputPort =
1170 llvm::any_of(firrtlPorts, [&](
auto p) {
return p.isOutput(); });
1171 if (!hasOutputPort &&
1174 newModule->setAttr(
"firrtl.extract.cover.extra", builder.getUnitAttr());
1180 loweringState.processRemainingAnnotations(oldModule, annos);
1185FIRRTLModuleLowering::lowerMemModule(FMemModuleOp oldModule,
1186 Block *topLevelModule,
1189 SmallVector<PortInfo> firrtlPorts = oldModule.getPorts();
1190 SmallVector<hw::PortInfo, 8> ports;
1191 if (failed(lowerPorts(firrtlPorts, ports, oldModule, oldModule.getName(),
1196 auto builder = OpBuilder::atBlockEnd(topLevelModule);
1198 oldModule.getLoc(), oldModule.getModuleNameAttr(), ports,
1199 oldModule.getModuleNameAttr());
1207FIRRTLModuleLowering::lowerModule(FModuleOp oldModule, Block *topLevelModule,
1210 SmallVector<PortInfo> firrtlPorts = oldModule.getPorts();
1211 SmallVector<hw::PortInfo, 8> ports;
1212 if (failed(lowerPorts(firrtlPorts, ports, oldModule, oldModule.getName(),
1217 auto builder = OpBuilder::atBlockEnd(topLevelModule);
1218 auto nameAttr = builder.getStringAttr(oldModule.getName());
1220 builder.create<
hw::HWModuleOp>(oldModule.getLoc(), nameAttr, ports);
1222 if (
auto comment = oldModule->getAttrOfType<StringAttr>(
"comment"))
1223 newModule.setCommentAttr(comment);
1226 SmallVector<StringRef, 12> attrNames = {
1227 "annotations",
"convention",
"layers",
1228 "portNames",
"sym_name",
"portDirections",
1229 "portTypes",
"portAnnotations",
"portSymbols",
1230 "portLocations",
"parameters", SymbolTable::getVisibilityAttrName()};
1232 DenseSet<StringRef> attrSet(attrNames.begin(), attrNames.end());
1233 SmallVector<NamedAttribute> newAttrs(newModule->getAttrs());
1235 llvm::make_filter_range(oldModule->getAttrs(), [&](auto namedAttr) {
1236 return !attrSet.count(namedAttr.getName()) &&
1237 !newModule->getAttrDictionary().contains(namedAttr.getName());
1239 newAttrs.push_back(i);
1241 newModule->setAttrs(newAttrs);
1245 SymbolTable::setSymbolVisibility(newModule,
1246 SymbolTable::getSymbolVisibility(oldModule));
1252 newModule->setAttr(
"firrtl.extract.cover.extra", builder.getUnitAttr());
1256 if (
auto testBenchDir =
loweringState.getTestBenchDirectory())
1258 if (!newModule->hasAttr(
"output_file"))
1259 newModule->setAttr(
"output_file", testBenchDir);
1260 newModule->setAttr(
"firrtl.extract.do_not_extract",
1261 builder.getUnitAttr());
1262 newModule.setCommentAttr(
1263 builder.getStringAttr(
"VCS coverage exclude_file"));
1269 loweringState.processRemainingAnnotations(oldModule, annos);
1278 Operation *insertPoint) {
1279 if (!value.hasOneUse())
1282 auto attach = dyn_cast<AttachOp>(*value.user_begin());
1283 if (!attach || attach.getNumOperands() != 2)
1287 auto loweredType =
lowerType(value.getType());
1288 if (loweredType.isInteger(0))
1293 auto attachedValue = attach.getOperand(attach.getOperand(0) == value);
1294 auto *op = attachedValue.getDefiningOp();
1295 if (op && op->getBlock() == insertPoint->getBlock() &&
1296 !op->isBeforeInBlock(insertPoint))
1301 ImplicitLocOpBuilder builder(insertPoint->getLoc(), insertPoint);
1317 if (type_isa<AnalogType>(flipValue.getType()))
1320 Operation *connectOp =
nullptr;
1321 for (
auto &use : flipValue.getUses()) {
1324 if (use.getOperandNumber() != 0)
1326 if (!isa<ConnectOp, MatchingConnectOp>(use.getOwner()))
1332 connectOp = use.getOwner();
1342 loweringState.lowerType(flipValue.getType(), flipValue.getLoc());
1343 if (loweredType.isInteger(0))
1348 ImplicitLocOpBuilder builder(insertPoint->getLoc(), insertPoint);
1350 auto connectSrc = connectOp->getOperand(1);
1353 if (!isa<FIRRTLType>(connectSrc.getType())) {
1359 if (!type_cast<FIRRTLBaseType>(connectSrc.getType()).isPassive())
1360 connectSrc = builder
1361 .create<mlir::UnrealizedConversionCastOp>(
1362 type_cast<FIRRTLBaseType>(connectSrc.getType())
1369 auto destTy = type_cast<FIRRTLBaseType>(flipValue.getType()).getPassiveType();
1371 if (destTy != connectSrc.getType() &&
1372 (isa<BaseTypeAliasType>(connectSrc.getType()) ||
1373 isa<BaseTypeAliasType>(destTy))) {
1375 builder.createOrFold<BitCastOp>(flipValue.getType(), connectSrc);
1377 if (!destTy.isGround()) {
1379 if (destTy != type_cast<FIRRTLType>(connectSrc.getType()))
1381 }
else if (destTy.getBitWidthOrSentinel() !=
1382 type_cast<FIRRTLBaseType>(connectSrc.getType())
1383 .getBitWidthOrSentinel()) {
1386 auto destWidth = destTy.getBitWidthOrSentinel();
1387 assert(destWidth != -1 &&
"must know integer widths");
1388 connectSrc = builder.createOrFold<PadPrimOp>(destTy, connectSrc, destWidth);
1400 SmallVector<SubfieldOp> accesses;
1401 for (
auto *op : structValue.getUsers()) {
1402 assert(isa<SubfieldOp>(op));
1403 auto fieldAccess = cast<SubfieldOp>(op);
1405 fieldAccess.getInput().getType().base().getElementIndex(field);
1406 if (elemIndex && *elemIndex == fieldAccess.getFieldIndex())
1407 accesses.push_back(fieldAccess);
1415LogicalResult FIRRTLModuleLowering::lowerModulePortsAndMoveBody(
1418 ImplicitLocOpBuilder bodyBuilder(oldModule.getLoc(), newModule.getBody());
1425 bodyBuilder.setInsertionPoint(cursor);
1428 SmallVector<PortInfo> firrtlPorts = oldModule.getPorts();
1429 assert(oldModule.getBody().getNumArguments() == firrtlPorts.size() &&
1430 "port count mismatch");
1432 SmallVector<Value, 4> outputs;
1435 auto *outputOp = newModule.getBodyBlock()->getTerminator();
1436 ImplicitLocOpBuilder outputBuilder(oldModule.getLoc(), outputOp);
1438 unsigned nextHWInputArg = 0;
1439 int hwPortIndex = -1;
1440 for (
auto [firrtlPortIndex, port] :
llvm::enumerate(firrtlPorts)) {
1442 auto oldArg = oldModule.getBody().getArgument(firrtlPortIndex);
1445 type_isa<FIRRTLBaseType>(port.type) &&
1446 type_cast<FIRRTLBaseType>(port.type).getBitWidthOrSentinel() == 0;
1450 if (!port.isOutput() && !isZeroWidth) {
1453 Value newArg = newModule.getBody().getArgument(nextHWInputArg++);
1459 oldArg.replaceAllUsesWith(newArg);
1465 if (isZeroWidth && port.isInput()) {
1466 Value newArg = bodyBuilder
1467 .create<WireOp>(port.type,
"." + port.getName().str() +
1470 oldArg.replaceAllUsesWith(newArg);
1478 outputs.push_back(value);
1479 assert(oldArg.use_empty() &&
"should have removed all uses of oldArg");
1485 auto newArg = bodyBuilder.create<WireOp>(
1486 port.type,
"." + port.getName().str() +
".output");
1489 oldArg.replaceAllUsesWith(newArg.getResult());
1492 auto resultHWType =
loweringState.lowerType(port.type, port.loc);
1493 if (!resultHWType.isInteger(0)) {
1496 outputs.push_back(output);
1499 if (
auto sym = newModule.getPort(hwPortIndex).getSym()) {
1500 newArg.setInnerSymAttr(sym);
1501 newModule.setPortSymbolAttr(hwPortIndex, {});
1507 outputOp->setOperands(outputs);
1510 auto &oldBlockInstList = oldModule.getBodyBlock()->getOperations();
1511 auto &newBlockInstList = newModule.getBodyBlock()->getOperations();
1512 newBlockInstList.splice(Block::iterator(cursor), oldBlockInstList,
1513 oldBlockInstList.begin(), oldBlockInstList.end());
1524FIRRTLModuleLowering::lowerFormalBody(verif::FormalOp newOp,
1526 auto builder = OpBuilder::atBlockEnd(&newOp.getBody().front());
1531 auto oldOp = cast<FormalOp>(
loweringState.getOldModule(newOp));
1532 auto moduleName = oldOp.getModuleNameAttr().getAttr();
1533 auto oldModule = cast<FModuleOp>(
1534 loweringState.getInstanceGraph().lookup(moduleName)->getModule());
1535 auto newModule = cast<hw::HWModuleOp>(
loweringState.getNewModule(oldModule));
1538 SmallVector<Value> symbolicInputs;
1539 for (
auto arg : newModule.getBody().getArguments())
1540 symbolicInputs.push_back(
1541 builder.create<
verif::SymbolicValueOp>(arg.
getLoc(), arg.getType()));
1544 builder.create<hw::InstanceOp>(newOp.getLoc(), newModule,
1545 newModule.getNameAttr(), symbolicInputs);
1552FIRRTLModuleLowering::lowerSimulationBody(verif::SimulationOp newOp,
1554 auto builder = OpBuilder::atBlockEnd(newOp.getBody());
1557 auto oldOp = cast<SimulationOp>(
loweringState.getOldModule(newOp));
1558 auto moduleName = oldOp.getModuleNameAttr().getAttr();
1559 auto oldModule = cast<FModuleLike>(
1560 *
loweringState.getInstanceGraph().lookup(moduleName)->getModule());
1562 cast<hw::HWModuleLike>(
loweringState.getNewModule(oldModule));
1566 SmallVector<Value> inputs(newOp.getBody()->args_begin(),
1567 newOp.getBody()->args_end());
1568 auto instOp = builder.create<hw::InstanceOp>(newOp.getLoc(), newModule,
1569 newModule.getNameAttr(), inputs);
1570 builder.create<verif::YieldOp>(newOp.getLoc(), instOp.getResults());
1580struct FIRRTLLowering :
public FIRRTLVisitor<FIRRTLLowering, LogicalResult> {
1582 FIRRTLLowering(
hw::HWModuleOp module, CircuitLoweringState &circuitState)
1583 : theModule(module), circuitState(circuitState),
1584 builder(module.
getLoc(), module.getContext()), moduleNamespace(module),
1585 backedgeBuilder(builder, module.
getLoc()) {}
1587 LogicalResult
run();
1590 Value getOrCreateClockConstant(seq::ClockConst clock);
1591 Value getOrCreateIntConstant(
const APInt &value);
1592 Value getOrCreateIntConstant(
unsigned numBits, uint64_t val,
1593 bool isSigned =
false) {
1594 return getOrCreateIntConstant(APInt(numBits, val, isSigned));
1596 Attribute getOrCreateAggregateConstantAttribute(Attribute value, Type type);
1597 Value getOrCreateXConstant(
unsigned numBits);
1598 Value getOrCreateZConstant(Type type);
1599 Value getPossiblyInoutLoweredValue(Value value);
1600 Value getLoweredValue(Value value);
1601 Value getLoweredNonClockValue(Value value);
1602 Value getLoweredAndExtendedValue(Value value, Type destType);
1603 Value getLoweredAndExtOrTruncValue(Value value, Type destType);
1604 LogicalResult setLowering(Value orig, Value result);
1605 LogicalResult setPossiblyFoldedLowering(Value orig, Value result);
1606 template <
typename ResultOpType,
typename... CtorArgTypes>
1607 LogicalResult setLoweringTo(Operation *orig, CtorArgTypes... args);
1608 template <
typename ResultOpType,
typename... CtorArgTypes>
1609 LogicalResult setLoweringToLTL(Operation *orig, CtorArgTypes... args);
1610 Backedge createBackedge(Location loc, Type type);
1611 Backedge createBackedge(Value orig, Type type);
1612 bool updateIfBackedge(Value dest, Value src);
1615 bool requiresInnerSymbol(hw::InnerSymbolOpInterface op) {
1620 if (
auto forceable = dyn_cast<Forceable>(op.getOperation()))
1621 if (forceable.isForceable())
1629 hw::InnerSymAttr lowerInnerSymbol(hw::InnerSymbolOpInterface op) {
1630 auto attr = op.getInnerSymAttr();
1634 if (requiresInnerSymbol(op))
1636 op.getContext(), attr, 0,
1641 void runWithInsertionPointAtEndOfBlock(
const std::function<
void(
void)> &fn,
1645 Value getReadValue(Value v);
1647 Value getNonClockValue(Value v);
1649 void addToAlwaysBlock(sv::EventControl clockEdge, Value clock,
1650 sv::ResetType resetStyle, sv::EventControl resetEdge,
1651 Value reset,
const std::function<
void(
void)> &body = {},
1652 const std::function<void(
void)> &resetBody = {});
1653 void addToAlwaysBlock(Value clock,
1654 const std::function<
void(
void)> &body = {}) {
1655 addToAlwaysBlock(sv::EventControl::AtPosEdge, clock, sv::ResetType(),
1656 sv::EventControl(), Value(), body,
1657 std::function<
void(
void)>());
1660 LogicalResult emitGuards(Location loc, ArrayRef<Attribute> guards,
1661 std::function<
void(
void)>
emit);
1662 void addToIfDefBlock(StringRef cond, std::function<
void(
void)> thenCtor,
1663 std::function<
void(
void)> elseCtor = {});
1664 void addToInitialBlock(std::function<
void(
void)> body);
1665 void addIfProceduralBlock(Value cond, std::function<
void(
void)> thenCtor,
1666 std::function<
void(
void)> elseCtor = {});
1667 Value getExtOrTruncAggregateValue(Value array,
FIRRTLBaseType sourceType,
1669 bool allowTruncate);
1670 Value createArrayIndexing(Value array, Value index);
1671 Value createValueWithMuxAnnotation(Operation *op,
bool isMux2);
1673 using FIRRTLVisitor<FIRRTLLowering, LogicalResult>::visitExpr;
1674 using FIRRTLVisitor<FIRRTLLowering, LogicalResult>::visitDecl;
1675 using FIRRTLVisitor<FIRRTLLowering, LogicalResult>::visitStmt;
1678 enum UnloweredOpResult { AlreadyLowered, NowLowered, LoweringFailure };
1679 UnloweredOpResult handleUnloweredOp(Operation *op);
1680 LogicalResult visitExpr(ConstantOp op);
1681 LogicalResult visitExpr(SpecialConstantOp op);
1682 LogicalResult visitExpr(SubindexOp op);
1683 LogicalResult visitExpr(SubaccessOp op);
1684 LogicalResult visitExpr(SubfieldOp op);
1685 LogicalResult visitExpr(VectorCreateOp op);
1686 LogicalResult visitExpr(BundleCreateOp op);
1687 LogicalResult visitExpr(FEnumCreateOp op);
1688 LogicalResult visitExpr(AggregateConstantOp op);
1689 LogicalResult visitExpr(IsTagOp op);
1690 LogicalResult visitExpr(SubtagOp op);
1691 LogicalResult visitExpr(TagExtractOp op);
1694 if (
auto castOp = dyn_cast<mlir::UnrealizedConversionCastOp>(op))
1695 return visitUnrealizedConversionCast(castOp);
1700 LogicalResult visitDecl(WireOp op);
1701 LogicalResult visitDecl(NodeOp op);
1702 LogicalResult visitDecl(RegOp op);
1703 LogicalResult visitDecl(RegResetOp op);
1704 LogicalResult visitDecl(MemOp op);
1705 LogicalResult visitDecl(InstanceOp oldInstance);
1706 LogicalResult visitDecl(VerbatimWireOp op);
1707 LogicalResult visitDecl(ContractOp op);
1710 LogicalResult lowerNoopCast(Operation *op);
1711 LogicalResult visitExpr(AsSIntPrimOp op);
1712 LogicalResult visitExpr(AsUIntPrimOp op);
1713 LogicalResult visitExpr(AsClockPrimOp op);
1714 LogicalResult visitExpr(AsAsyncResetPrimOp op) {
return lowerNoopCast(op); }
1716 LogicalResult visitExpr(HWStructCastOp op);
1717 LogicalResult visitExpr(BitCastOp op);
1719 visitUnrealizedConversionCast(mlir::UnrealizedConversionCastOp op);
1720 LogicalResult visitExpr(CvtPrimOp op);
1721 LogicalResult visitExpr(NotPrimOp op);
1722 LogicalResult visitExpr(NegPrimOp op);
1723 LogicalResult visitExpr(PadPrimOp op);
1724 LogicalResult visitExpr(XorRPrimOp op);
1725 LogicalResult visitExpr(AndRPrimOp op);
1726 LogicalResult visitExpr(OrRPrimOp op);
1729 template <
typename ResultUnsignedOpType,
1730 typename ResultSignedOpType = ResultUnsignedOpType>
1731 LogicalResult lowerBinOp(Operation *op);
1732 template <
typename ResultOpType>
1733 LogicalResult lowerBinOpToVariadic(Operation *op);
1735 template <
typename ResultOpType>
1736 LogicalResult lowerElementwiseLogicalOp(Operation *op);
1738 LogicalResult lowerCmpOp(Operation *op, ICmpPredicate signedOp,
1739 ICmpPredicate unsignedOp);
1740 template <
typename SignedOp,
typename Un
signedOp>
1741 LogicalResult lowerDivLikeOp(Operation *op);
1743 LogicalResult visitExpr(CatPrimOp op);
1745 LogicalResult visitExpr(AndPrimOp op) {
1746 return lowerBinOpToVariadic<comb::AndOp>(op);
1748 LogicalResult visitExpr(OrPrimOp op) {
1749 return lowerBinOpToVariadic<comb::OrOp>(op);
1751 LogicalResult visitExpr(XorPrimOp op) {
1752 return lowerBinOpToVariadic<comb::XorOp>(op);
1754 LogicalResult visitExpr(ElementwiseOrPrimOp op) {
1755 return lowerElementwiseLogicalOp<comb::OrOp>(op);
1757 LogicalResult visitExpr(ElementwiseAndPrimOp op) {
1758 return lowerElementwiseLogicalOp<comb::AndOp>(op);
1760 LogicalResult visitExpr(ElementwiseXorPrimOp op) {
1761 return lowerElementwiseLogicalOp<comb::XorOp>(op);
1763 LogicalResult visitExpr(AddPrimOp op) {
1764 return lowerBinOpToVariadic<comb::AddOp>(op);
1766 LogicalResult visitExpr(EQPrimOp op) {
1767 return lowerCmpOp(op, ICmpPredicate::eq, ICmpPredicate::eq);
1769 LogicalResult visitExpr(NEQPrimOp op) {
1770 return lowerCmpOp(op, ICmpPredicate::ne, ICmpPredicate::ne);
1772 LogicalResult visitExpr(LTPrimOp op) {
1773 return lowerCmpOp(op, ICmpPredicate::slt, ICmpPredicate::ult);
1775 LogicalResult visitExpr(LEQPrimOp op) {
1776 return lowerCmpOp(op, ICmpPredicate::sle, ICmpPredicate::ule);
1778 LogicalResult visitExpr(GTPrimOp op) {
1779 return lowerCmpOp(op, ICmpPredicate::sgt, ICmpPredicate::ugt);
1781 LogicalResult visitExpr(GEQPrimOp op) {
1782 return lowerCmpOp(op, ICmpPredicate::sge, ICmpPredicate::uge);
1785 LogicalResult visitExpr(SubPrimOp op) {
return lowerBinOp<comb::SubOp>(op); }
1786 LogicalResult visitExpr(MulPrimOp op) {
1787 return lowerBinOpToVariadic<comb::MulOp>(op);
1789 LogicalResult visitExpr(DivPrimOp op) {
1790 return lowerDivLikeOp<comb::DivSOp, comb::DivUOp>(op);
1792 LogicalResult visitExpr(RemPrimOp op) {
1793 return lowerDivLikeOp<comb::ModSOp, comb::ModUOp>(op);
1797 LogicalResult visitExpr(IsXIntrinsicOp op);
1798 LogicalResult visitExpr(PlusArgsTestIntrinsicOp op);
1799 LogicalResult visitExpr(PlusArgsValueIntrinsicOp op);
1800 LogicalResult visitStmt(FPGAProbeIntrinsicOp op);
1801 LogicalResult visitExpr(ClockInverterIntrinsicOp op);
1802 LogicalResult visitExpr(ClockDividerIntrinsicOp op);
1803 LogicalResult visitExpr(SizeOfIntrinsicOp op);
1804 LogicalResult visitExpr(ClockGateIntrinsicOp op);
1805 LogicalResult visitExpr(LTLAndIntrinsicOp op);
1806 LogicalResult visitExpr(LTLOrIntrinsicOp op);
1807 LogicalResult visitExpr(LTLIntersectIntrinsicOp op);
1808 LogicalResult visitExpr(LTLDelayIntrinsicOp op);
1809 LogicalResult visitExpr(LTLConcatIntrinsicOp op);
1810 LogicalResult visitExpr(LTLRepeatIntrinsicOp op);
1811 LogicalResult visitExpr(LTLGoToRepeatIntrinsicOp op);
1812 LogicalResult visitExpr(LTLNonConsecutiveRepeatIntrinsicOp op);
1813 LogicalResult visitExpr(LTLNotIntrinsicOp op);
1814 LogicalResult visitExpr(LTLImplicationIntrinsicOp op);
1815 LogicalResult visitExpr(LTLUntilIntrinsicOp op);
1816 LogicalResult visitExpr(LTLEventuallyIntrinsicOp op);
1817 LogicalResult visitExpr(LTLClockIntrinsicOp op);
1819 template <
typename TargetOp,
typename IntrinsicOp>
1820 LogicalResult lowerVerifIntrinsicOp(IntrinsicOp op);
1821 LogicalResult visitStmt(VerifAssertIntrinsicOp op);
1822 LogicalResult visitStmt(VerifAssumeIntrinsicOp op);
1823 LogicalResult visitStmt(VerifCoverIntrinsicOp op);
1824 LogicalResult visitStmt(VerifRequireIntrinsicOp op);
1825 LogicalResult visitStmt(VerifEnsureIntrinsicOp op);
1826 LogicalResult visitExpr(HasBeenResetIntrinsicOp op);
1827 LogicalResult visitStmt(UnclockedAssumeIntrinsicOp op);
1830 LogicalResult visitExpr(BitsPrimOp op);
1831 LogicalResult visitExpr(InvalidValueOp op);
1832 LogicalResult visitExpr(HeadPrimOp op);
1833 LogicalResult visitExpr(ShlPrimOp op);
1834 LogicalResult visitExpr(ShrPrimOp op);
1835 LogicalResult visitExpr(DShlPrimOp op) {
1836 return lowerDivLikeOp<comb::ShlOp, comb::ShlOp>(op);
1838 LogicalResult visitExpr(DShrPrimOp op) {
1839 return lowerDivLikeOp<comb::ShrSOp, comb::ShrUOp>(op);
1841 LogicalResult visitExpr(DShlwPrimOp op) {
1842 return lowerDivLikeOp<comb::ShlOp, comb::ShlOp>(op);
1844 LogicalResult visitExpr(TailPrimOp op);
1845 LogicalResult visitExpr(MuxPrimOp op);
1846 LogicalResult visitExpr(Mux2CellIntrinsicOp op);
1847 LogicalResult visitExpr(Mux4CellIntrinsicOp op);
1848 LogicalResult visitExpr(MultibitMuxOp op);
1849 LogicalResult visitExpr(VerbatimExprOp op);
1850 LogicalResult visitExpr(XMRRefOp op);
1851 LogicalResult visitExpr(XMRDerefOp op);
1854 LogicalResult visitExpr(TimeOp op);
1855 LogicalResult visitExpr(HierarchicalModuleNameOp op);
1858 LogicalResult lowerVerificationStatement(
1859 Operation *op, StringRef labelPrefix, Value clock, Value predicate,
1860 Value enable, StringAttr messageAttr, ValueRange operands,
1861 StringAttr nameAttr,
bool isConcurrent, EventControl eventControl);
1863 LogicalResult visitStmt(SkipOp op);
1865 FailureOr<bool> lowerConnect(Value dest, Value srcVal);
1866 LogicalResult visitStmt(ConnectOp op);
1867 LogicalResult visitStmt(MatchingConnectOp op);
1868 LogicalResult visitStmt(ForceOp op);
1870 std::optional<Value> getLoweredFmtOperand(Value operand);
1871 LogicalResult loweredFmtOperands(ValueRange operands,
1872 SmallVectorImpl<Value> &loweredOperands);
1873 FailureOr<Value> callFileDescriptorLib(
const FileDescriptorInfo &info);
1877 LogicalResult lowerStatementWithFd(
1878 const FileDescriptorInfo &fileDescriptorInfo, Value clock, Value cond,
1879 const std::function<LogicalResult(Value)> &fn,
bool usePrintfCond);
1883 LogicalResult visitPrintfLike(T op,
1884 const FileDescriptorInfo &fileDescriptorInfo,
1885 bool usePrintfCond);
1886 LogicalResult visitStmt(PrintFOp op) {
return visitPrintfLike(op, {},
true); }
1887 LogicalResult visitStmt(FPrintFOp op);
1888 LogicalResult visitStmt(FFlushOp op);
1889 LogicalResult visitStmt(StopOp op);
1890 LogicalResult visitStmt(AssertOp op);
1891 LogicalResult visitStmt(AssumeOp op);
1892 LogicalResult visitStmt(CoverOp op);
1893 LogicalResult visitStmt(AttachOp op);
1894 LogicalResult visitStmt(RefForceOp op);
1895 LogicalResult visitStmt(RefForceInitialOp op);
1896 LogicalResult visitStmt(RefReleaseOp op);
1897 LogicalResult visitStmt(RefReleaseInitialOp op);
1898 LogicalResult visitStmt(BindOp op);
1900 FailureOr<Value> lowerSubindex(SubindexOp op, Value input);
1901 FailureOr<Value> lowerSubaccess(SubaccessOp op, Value input);
1902 FailureOr<Value> lowerSubfield(SubfieldOp op, Value input);
1904 LogicalResult fixupLTLOps();
1907 return circuitState.lowerType(type, builder.getLoc());
1915 CircuitLoweringState &circuitState;
1918 ImplicitLocOpBuilder builder;
1923 DenseMap<Value, Value> valueMapping;
1927 DenseMap<Value, Value> fromClockMapping;
1931 DenseMap<Attribute, Value> hwConstantMap;
1932 DenseMap<std::pair<Attribute, Type>, Attribute> hwAggregateConstantMap;
1936 DenseMap<unsigned, Value> hwConstantXMap;
1937 DenseMap<Type, Value> hwConstantZMap;
1943 DenseMap<Value, Value> readInOutCreated;
1946 DenseMap<StringAttr, sv::RegOp> fileNameToFileDescriptor;
1950 using AlwaysKeyType = std::tuple<
Block *, sv::EventControl, Value,
1951 sv::ResetType, sv::EventControl, Value>;
1968 llvm::MapVector<Value, Value> backedges;
1975 DenseSet<Operation *> maybeUnusedValues;
1977 void maybeUnused(Operation *op) { maybeUnusedValues.insert(op); }
1978 void maybeUnused(Value value) {
1979 if (
auto *op = value.getDefiningOp())
1991 SetVector<Operation *> ltlOpFixupWorklist;
1996 SmallVector<std::pair<Block::iterator, Block::iterator>> worklist;
1998 void addToWorklist(Block &block) {
1999 worklist.push_back({block.begin(), block.end()});
2001 void addToWorklist(Region ®ion) {
2002 for (
auto &block :
llvm::reverse(region))
2003 addToWorklist(block);
2014LogicalResult FIRRTLModuleLowering::lowerFileBody(emit::FileOp fileOp) {
2015 OpBuilder b(&getContext());
2016 fileOp->walk([&](Operation *op) {
2017 if (
auto bindOp = dyn_cast<BindOp>(op)) {
2018 b.setInsertionPointAfter(bindOp);
2019 b.create<sv::BindOp>(bindOp.getLoc(), bindOp.getInstanceAttr());
2027FIRRTLModuleLowering::lowerBody(Operation *op,
2029 if (
auto moduleOp = dyn_cast<hw::HWModuleOp>(op))
2031 if (
auto formalOp = dyn_cast<verif::FormalOp>(op))
2033 if (
auto simulationOp = dyn_cast<verif::SimulationOp>(op))
2035 if (
auto fileOp = dyn_cast<emit::FileOp>(op))
2036 return lowerFileBody(fileOp);
2041LogicalResult FIRRTLLowering::run() {
2044 for (
auto arg : theModule.
getBodyBlock()->getArguments())
2045 if (failed(setLowering(arg, arg)))
2052 addToWorklist(theModule.getBody());
2053 SmallVector<Operation *, 16> opsToRemove;
2055 while (!worklist.empty()) {
2056 auto &[opsIt, opsEnd] = worklist.back();
2057 if (opsIt == opsEnd) {
2058 worklist.pop_back();
2061 Operation *op = &*opsIt++;
2063 builder.setInsertionPoint(op);
2064 builder.setLoc(op->getLoc());
2065 auto done = succeeded(dispatchVisitor(op));
2066 circuitState.processRemainingAnnotations(op,
AnnotationSet(op));
2068 opsToRemove.push_back(op);
2070 switch (handleUnloweredOp(op)) {
2071 case AlreadyLowered:
2074 opsToRemove.push_back(op);
2076 case LoweringFailure:
2088 for (
auto &[backedge, value] : backedges) {
2089 SmallVector<Location> driverLocs;
2095 if (backedge == value) {
2096 Location edgeLoc = backedge.getLoc();
2097 if (driverLocs.empty()) {
2098 mlir::emitError(edgeLoc,
"sink does not have a driver");
2100 auto diag = mlir::emitError(edgeLoc,
"sink in combinational loop");
2101 for (
auto loc : driverLocs)
2102 diag.attachNote(loc) <<
"through driver here";
2108 auto *it = backedges.find(value);
2109 if (it == backedges.end())
2112 driverLocs.push_back(value.getLoc());
2115 if (
auto *defOp = backedge.getDefiningOp())
2116 maybeUnusedValues.erase(defOp);
2117 backedge.replaceAllUsesWith(value);
2125 while (!opsToRemove.empty()) {
2126 auto *op = opsToRemove.pop_back_val();
2131 for (
auto result : op->getResults()) {
2135 auto builder = OpBuilder::atBlockBegin(theModule.getBodyBlock());
2137 builder.getIntegerType(0), 0);
2138 maybeUnusedValues.insert(zeroI0);
2140 result.replaceAllUsesWith(zeroI0);
2143 if (!op->use_empty()) {
2144 auto d = op->emitOpError(
2145 "still has uses; should remove ops in reverse order of visitation");
2146 SmallPtrSet<Operation *, 2> visited;
2147 for (
auto *user : op->getUsers())
2148 if (visited.insert(user).second)
2149 d.attachNote(user->
getLoc())
2150 <<
"used by " << user->
getName() <<
" op";
2153 maybeUnusedValues.erase(op);
2158 while (!maybeUnusedValues.empty()) {
2159 auto it = maybeUnusedValues.begin();
2161 maybeUnusedValues.erase(it);
2162 if (!isOpTriviallyDead(op))
2164 for (
auto operand : op->getOperands())
2165 if (auto *defOp = operand.getDefiningOp())
2166 maybeUnusedValues.insert(defOp);
2172 if (failed(fixupLTLOps()))
2183Value FIRRTLLowering::getOrCreateClockConstant(seq::ClockConst clock) {
2184 auto attr = seq::ClockConstAttr::get(theModule.getContext(), clock);
2186 auto &entry = hwConstantMap[attr];
2190 OpBuilder entryBuilder(&theModule.getBodyBlock()->front());
2191 entry = entryBuilder.create<seq::ConstClockOp>(builder.getLoc(), attr);
2197Value FIRRTLLowering::getOrCreateIntConstant(
const APInt &value) {
2198 auto attr = builder.getIntegerAttr(
2199 builder.getIntegerType(value.getBitWidth()), value);
2201 auto &entry = hwConstantMap[attr];
2205 OpBuilder entryBuilder(&theModule.getBodyBlock()->front());
2206 entry = entryBuilder.create<
hw::ConstantOp>(builder.getLoc(), attr);
2212Attribute FIRRTLLowering::getOrCreateAggregateConstantAttribute(Attribute value,
2215 if (hw::type_isa<IntegerType>(type))
2216 return builder.getIntegerAttr(type, cast<IntegerAttr>(value).getValue());
2218 auto cache = hwAggregateConstantMap.lookup({value, type});
2223 SmallVector<Attribute> values;
2224 for (
auto e :
llvm::enumerate(cast<ArrayAttr>(value))) {
2226 if (
auto array = hw::type_dyn_cast<hw::ArrayType>(type))
2227 subType = array.getElementType();
2228 else if (
auto structType = hw::type_dyn_cast<hw::StructType>(type))
2229 subType = structType.getElements()[e.index()].type;
2231 assert(
false &&
"type must be either array or struct");
2233 values.push_back(getOrCreateAggregateConstantAttribute(e.value(), subType));
2237 if (hw::type_isa<hw::ArrayType>(type))
2238 std::reverse(values.begin(), values.end());
2240 auto &entry = hwAggregateConstantMap[{value, type}];
2241 entry = builder.getArrayAttr(values);
2249 const std::function<LogicalResult()> &fn) {
2250 assert(failedOperand &&
"Should be called on the failed operand");
2258Value FIRRTLLowering::getOrCreateXConstant(
unsigned numBits) {
2260 auto &entry = hwConstantXMap[numBits];
2264 OpBuilder entryBuilder(&theModule.getBodyBlock()->front());
2265 entry = entryBuilder.create<sv::ConstantXOp>(
2266 builder.getLoc(), entryBuilder.getIntegerType(numBits));
2270Value FIRRTLLowering::getOrCreateZConstant(Type type) {
2271 auto &entry = hwConstantZMap[type];
2273 OpBuilder entryBuilder(&theModule.getBodyBlock()->front());
2274 entry = entryBuilder.create<sv::ConstantZOp>(builder.getLoc(), type);
2283Value FIRRTLLowering::getPossiblyInoutLoweredValue(Value value) {
2285 if (
auto lowering = valueMapping.lookup(value)) {
2286 assert(!isa<FIRRTLType>(lowering.getType()) &&
2287 "Lowered value should be a non-FIRRTL value");
2296Value FIRRTLLowering::getLoweredValue(Value value) {
2297 auto result = getPossiblyInoutLoweredValue(value);
2303 if (isa<hw::InOutType>(result.getType()))
2304 return getReadValue(result);
2310Value FIRRTLLowering::getLoweredNonClockValue(Value value) {
2311 auto result = getLoweredValue(value);
2315 if (hw::type_isa<seq::ClockType>(result.getType()))
2316 return getNonClockValue(result);
2324Value FIRRTLLowering::getExtOrTruncAggregateValue(Value array,
2327 bool allowTruncate) {
2328 SmallVector<Value> resultBuffer;
2333 auto srcWidth = firrtl::type_cast<IntType>(sourceType).getWidthOrSentinel();
2334 auto destWidth = firrtl::type_cast<IntType>(destType).getWidthOrSentinel();
2335 auto resultType = builder.getIntegerType(destWidth);
2337 if (srcWidth == destWidth)
2340 if (srcWidth > destWidth) {
2344 builder.emitError(
"operand should not be a truncation");
2348 if (firrtl::type_cast<IntType>(sourceType).isSigned())
2349 return comb::createOrFoldSExt(value, resultType, builder);
2350 auto zero = getOrCreateIntConstant(destWidth - srcWidth, 0);
2358 return TypeSwitch<FIRRTLBaseType, LogicalResult>(srcType)
2359 .Case<FVectorType>([&](
auto srcVectorType) {
2360 auto destVectorType = firrtl::type_cast<FVectorType>(destType);
2361 unsigned size = resultBuffer.size();
2362 unsigned indexWidth =
2364 for (
size_t i = 0, e = std::min(srcVectorType.getNumElements(),
2365 destVectorType.getNumElements());
2367 auto iIdx = getOrCreateIntConstant(indexWidth, i);
2369 if (failed(recurse(arrayIndex, srcVectorType.getElementType(),
2370 destVectorType.getElementType())))
2373 SmallVector<Value> temp(resultBuffer.begin() + size,
2374 resultBuffer.end());
2376 resultBuffer.resize(size);
2377 resultBuffer.push_back(array);
2380 .Case<BundleType>([&](BundleType srcStructType) {
2381 auto destStructType = firrtl::type_cast<BundleType>(destType);
2382 unsigned size = resultBuffer.size();
2385 if (destStructType.getNumElements() != srcStructType.getNumElements())
2388 for (
auto elem :
llvm::enumerate(destStructType)) {
2389 auto structExtract =
2391 if (failed(recurse(structExtract,
2392 srcStructType.getElementType(elem.index()),
2393 destStructType.getElementType(elem.index()))))
2396 SmallVector<Value> temp(resultBuffer.begin() + size,
2397 resultBuffer.end());
2400 resultBuffer.resize(size);
2401 resultBuffer.push_back(newStruct);
2404 .Case<IntType>([&](
auto) {
2405 if (
auto result = cast(src, srcType, destType)) {
2406 resultBuffer.push_back(result);
2411 .Default([&](
auto) {
return failure(); });
2414 if (failed(recurse(array, sourceType, destType)))
2417 assert(resultBuffer.size() == 1 &&
2418 "resultBuffer must only contain a result array if `success` is true");
2419 return resultBuffer[0];
2427Value FIRRTLLowering::getLoweredAndExtendedValue(Value src, Type target) {
2428 auto srcType = cast<FIRRTLBaseType>(src.getType());
2429 auto dstType = cast<FIRRTLBaseType>(target);
2430 auto loweredSrc = getLoweredValue(src);
2433 auto dstWidth = dstType.getBitWidthOrSentinel();
2449 return getOrCreateIntConstant(dstWidth, 0);
2452 auto loweredSrcType = loweredSrc.getType();
2453 auto loweredDstType =
lowerType(dstType);
2456 if (loweredSrcType == loweredDstType)
2460 if (dstWidth == srcType.getBitWidthOrSentinel()) {
2462 if (loweredSrcType != loweredDstType &&
2463 (isa<hw::TypeAliasType>(loweredSrcType) ||
2464 isa<hw::TypeAliasType>(loweredDstType))) {
2465 return builder.createOrFold<
hw::BitcastOp>(loweredDstType, loweredSrc);
2470 if (isa<hw::ArrayType, hw::StructType>(loweredSrcType))
2471 return getExtOrTruncAggregateValue(loweredSrc, srcType, dstType,
2474 if (isa<seq::ClockType>(loweredSrcType)) {
2475 builder.emitError(
"cannot use clock type as an integer");
2479 auto intSourceType = dyn_cast<IntegerType>(loweredSrcType);
2480 if (!intSourceType) {
2481 builder.emitError(
"operand of type ")
2482 << loweredSrcType <<
" cannot be used as an integer";
2486 auto loweredSrcWidth = intSourceType.getWidth();
2487 if (loweredSrcWidth ==
unsigned(dstWidth))
2490 if (loweredSrcWidth >
unsigned(dstWidth)) {
2491 builder.emitError(
"operand should not be a truncation");
2496 auto valueFIRType = type_cast<FIRRTLBaseType>(src.getType()).getPassiveType();
2497 if (type_cast<IntType>(valueFIRType).isSigned())
2498 return comb::createOrFoldSExt(loweredSrc, loweredDstType, builder);
2500 auto zero = getOrCreateIntConstant(dstWidth - loweredSrcWidth, 0);
2509Value FIRRTLLowering::getLoweredAndExtOrTruncValue(Value value, Type destType) {
2510 assert(type_isa<FIRRTLBaseType>(value.getType()) &&
2511 type_isa<FIRRTLBaseType>(destType) &&
2512 "input/output value should be FIRRTL");
2515 auto destWidth = type_cast<FIRRTLBaseType>(destType).getBitWidthOrSentinel();
2516 if (destWidth == -1)
2519 auto result = getLoweredValue(value);
2531 return getOrCreateIntConstant(destWidth, 0);
2535 if (isa<hw::ArrayType, hw::StructType>(result.getType())) {
2537 if (destType == value.getType())
2540 return getExtOrTruncAggregateValue(
2541 result, type_cast<FIRRTLBaseType>(value.getType()),
2542 type_cast<FIRRTLBaseType>(destType),
2546 auto srcWidth = type_cast<IntegerType>(result.getType()).getWidth();
2547 if (srcWidth ==
unsigned(destWidth))
2553 if (srcWidth >
unsigned(destWidth)) {
2554 auto resultType = builder.getIntegerType(destWidth);
2558 auto resultType = builder.getIntegerType(destWidth);
2562 type_cast<FIRRTLBaseType>(value.getType()).getPassiveType();
2563 if (type_cast<IntType>(valueFIRType).isSigned())
2564 return comb::createOrFoldSExt(result, resultType, builder);
2566 auto zero = getOrCreateIntConstant(destWidth - srcWidth, 0);
2580std::optional<Value> FIRRTLLowering::getLoweredFmtOperand(Value operand) {
2582 if (type_isa<FStringType>(operand.getType())) {
2583 if (isa<TimeOp>(operand.getDefiningOp()))
2584 return builder.create<sv::TimeOp>();
2585 if (isa<HierarchicalModuleNameOp>(operand.getDefiningOp()))
2589 auto loweredValue = getLoweredValue(operand);
2590 if (!loweredValue) {
2594 loweredValue = getOrCreateIntConstant(1, 0);
2599 if (
auto intTy = firrtl::type_cast<IntType>(operand.getType()))
2600 if (intTy.isSigned())
2601 loweredValue = builder.create<sv::SystemFunctionOp>(
2602 loweredValue.getType(),
"signed", loweredValue);
2604 return loweredValue;
2608FIRRTLLowering::loweredFmtOperands(mlir::ValueRange operands,
2609 SmallVectorImpl<Value> &loweredOperands) {
2610 for (
auto operand : operands) {
2611 std::optional<Value> loweredValue = getLoweredFmtOperand(operand);
2616 loweredOperands.push_back(*loweredValue);
2621LogicalResult FIRRTLLowering::lowerStatementWithFd(
2622 const FileDescriptorInfo &fileDescriptor, Value clock, Value cond,
2623 const std::function<LogicalResult(Value)> &fn,
bool usePrintfCond) {
2625 bool failed =
false;
2626 circuitState.addMacroDecl(builder.getStringAttr(
"SYNTHESIS"));
2627 addToIfDefBlock(
"SYNTHESIS", std::function<
void()>(), [&]() {
2628 addToAlwaysBlock(clock, [&]() {
2631 circuitState.usedPrintf =
true;
2633 circuitState.addFragment(theModule,
"PRINTF_COND_FRAGMENT");
2636 Value ifCond = cond;
2637 if (usePrintfCond) {
2639 builder.create<sv::MacroRefExprOp>(cond.getType(),
"PRINTF_COND_");
2640 ifCond = builder.createOrFold<
comb::AndOp>(ifCond, cond,
true);
2643 addIfProceduralBlock(ifCond, [&]() {
2647 if (fileDescriptor.isDefaultFd()) {
2649 fd = builder.create<hw::ConstantOp>(APInt(32, 0x80000002));
2652 auto fdOrError = callFileDescriptorLib(fileDescriptor);
2653 if (llvm::failed(fdOrError)) {
2659 failed = llvm::failed(fn(fd));
2663 return failure(failed);
2667FIRRTLLowering::callFileDescriptorLib(
const FileDescriptorInfo &info) {
2668 circuitState.usedFileDescriptorLib =
true;
2669 circuitState.addFragment(theModule,
"CIRCT_LIB_LOGGING_FRAGMENT");
2672 if (
info.isSubstitutionRequired()) {
2673 SmallVector<Value> fileNameOperands;
2674 if (failed(loweredFmtOperands(
info.getSubstitutions(), fileNameOperands)))
2678 .create<sv::SFormatFOp>(
info.getOutputFileFormat(),
2683 fileName = builder.create<sv::ConstantStrOp>(
info.getOutputFileFormat())
2688 .create<sv::FuncCallProceduralOp>(
2689 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 = builder.create<seq::FromClockOp>(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 = builder.create<sv::IfOp>(
2899 reset, []() {}, []() {});
2901 if (resetStyle == sv::ResetType::AsyncReset) {
2902 sv::EventControl events[] = {clockEdge, resetEdge};
2903 Value clocks[] = {clock, reset};
2905 alwaysOp = builder.create<sv::AlwaysOp>(events, clocks, [&]() {
2906 if (resetEdge == sv::EventControl::AtNegEdge)
2907 llvm_unreachable(
"negative edge for reset is not expected");
2911 alwaysOp = builder.create<sv::AlwaysOp>(clockEdge, clock, createIfOp);
2915 alwaysOp = builder.create<sv::AlwaysOp>(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 builder.create<
sv::IfDefOp>(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()] = builder.create<sv::InitialOp>(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 builder.create<sv::IfOp>(cond, thenCtor, elseCtor);
3017FIRRTLLowering::UnloweredOpResult
3018FIRRTLLowering::handleUnloweredOp(Operation *op) {
3020 if (!op->getRegions().empty() && isa<FIRRTLDialect>(op->getDialect())) {
3021 op->emitOpError(
"must explicitly handle its regions");
3022 return LoweringFailure;
3029 if (!isa<FIRRTLDialect>(op->getDialect())) {
3031 for (
auto ®ion : op->getRegions())
3032 addToWorklist(region);
3033 for (
auto &operand : op->getOpOperands())
3034 if (auto lowered = getPossiblyInoutLoweredValue(operand.
get()))
3035 operand.set(lowered);
3036 for (
auto result : op->getResults())
3037 (void)setLowering(result, result);
3038 return AlreadyLowered;
3050 if (op->getNumResults() == 1) {
3051 auto resultType = op->getResult(0).getType();
3052 if (type_isa<FIRRTLBaseType>(resultType) &&
3054 (
isExpression(op) || isa<mlir::UnrealizedConversionCastOp>(op))) {
3056 (void)setLowering(op->getResult(0), Value());
3060 op->emitOpError(
"LowerToHW couldn't handle this operation");
3061 return LoweringFailure;
3064LogicalResult FIRRTLLowering::visitExpr(ConstantOp op) {
3067 return setLowering(op, Value());
3069 return setLowering(op, getOrCreateIntConstant(op.getValue()));
3072LogicalResult FIRRTLLowering::visitExpr(SpecialConstantOp op) {
3074 if (isa<ClockType>(op.getType())) {
3075 cst = getOrCreateClockConstant(op.getValue() ? seq::ClockConst::High
3076 :
seq::ClockConst::Low);
3078 cst = getOrCreateIntConstant(APInt( 1, op.getValue()));
3080 return setLowering(op, cst);
3083FailureOr<Value> FIRRTLLowering::lowerSubindex(SubindexOp op, Value input) {
3084 auto iIdx = getOrCreateIntConstant(
3086 firrtl::type_cast<FVectorType>(op.getInput().getType())
3093 if (isa<sv::InOutType>(input.getType()))
3094 result = builder.createOrFold<sv::ArrayIndexInOutOp>(input, iIdx);
3097 if (
auto *definingOp = result.getDefiningOp())
3102FailureOr<Value> FIRRTLLowering::lowerSubaccess(SubaccessOp op, Value input) {
3103 Value valueIdx = getLoweredAndExtOrTruncValue(
3105 UIntType::get(op->getContext(),
3107 firrtl::type_cast<FVectorType>(op.getInput().getType())
3108 .getNumElements())));
3110 op->emitError() <<
"input lowering failed";
3117 if (isa<sv::InOutType>(input.getType()))
3118 result = builder.createOrFold<sv::ArrayIndexInOutOp>(input, valueIdx);
3120 result = createArrayIndexing(input, valueIdx);
3121 if (
auto *definingOp = result.getDefiningOp())
3126FailureOr<Value> FIRRTLLowering::lowerSubfield(SubfieldOp op, Value input) {
3127 auto resultType =
lowerType(op->getResult(0).getType());
3128 if (!resultType || !input) {
3129 op->emitError() <<
"subfield type lowering failed";
3135 auto field = firrtl::type_cast<BundleType>(op.getInput().getType())
3136 .getElementName(op.getFieldIndex());
3138 if (isa<sv::InOutType>(input.getType()))
3139 result = builder.createOrFold<sv::StructFieldInOutOp>(input, field);
3142 if (
auto *definingOp = result.getDefiningOp())
3147LogicalResult FIRRTLLowering::visitExpr(SubindexOp op) {
3149 return setLowering(op, Value());
3151 auto input = getPossiblyInoutLoweredValue(op.getInput());
3153 return op.emitError() <<
"input lowering failed";
3155 auto result = lowerSubindex(op, input);
3158 return setLowering(op, *result);
3161LogicalResult FIRRTLLowering::visitExpr(SubaccessOp op) {
3163 return setLowering(op, Value());
3165 auto input = getPossiblyInoutLoweredValue(op.getInput());
3167 return op.emitError() <<
"input lowering failed";
3169 auto result = lowerSubaccess(op, input);
3172 return setLowering(op, *result);
3175LogicalResult FIRRTLLowering::visitExpr(SubfieldOp op) {
3178 if (getLoweredValue(op) || !op.getInput())
3182 return setLowering(op, Value());
3184 auto input = getPossiblyInoutLoweredValue(op.getInput());
3186 return op.emitError() <<
"input lowering failed";
3188 auto result = lowerSubfield(op, input);
3191 return setLowering(op, *result);
3194LogicalResult FIRRTLLowering::visitExpr(VectorCreateOp op) {
3195 auto resultType =
lowerType(op.getResult().getType());
3196 SmallVector<Value> operands;
3198 for (
auto oper :
llvm::reverse(op.getOperands())) {
3199 auto val = getLoweredValue(oper);
3202 operands.push_back(val);
3204 return setLoweringTo<hw::ArrayCreateOp>(op, resultType, operands);
3207LogicalResult FIRRTLLowering::visitExpr(BundleCreateOp op) {
3208 auto resultType =
lowerType(op.getResult().getType());
3209 SmallVector<Value> operands;
3210 for (
auto oper : op.getOperands()) {
3211 auto val = getLoweredValue(oper);
3214 operands.push_back(val);
3216 return setLoweringTo<hw::StructCreateOp>(op, resultType, operands);
3219LogicalResult FIRRTLLowering::visitExpr(FEnumCreateOp op) {
3222 return setLowering(op, Value());
3224 auto input = getLoweredValue(op.getInput());
3225 auto tagName = op.getFieldNameAttr();
3226 auto oldType = op.getType().base();
3228 auto element = *oldType.getElement(op.getFieldNameAttr());
3230 if (
auto structType = dyn_cast<hw::StructType>(newType)) {
3231 auto tagType = structType.getFieldType(
"tag");
3232 auto tagValue = IntegerAttr::get(tagType, element.value.getValue());
3233 auto tag = builder.create<sv::LocalParamOp>(op.getLoc(), tagType, tagValue,
3235 auto bodyType = structType.getFieldType(
"body");
3236 auto body = builder.create<hw::UnionCreateOp>(bodyType, tagName, input);
3237 SmallVector<Value> operands = {tag.getResult(), body.getResult()};
3238 return setLoweringTo<hw::StructCreateOp>(op, structType, operands);
3240 auto tagValue = IntegerAttr::get(newType, element.value.getValue());
3241 return setLoweringTo<sv::LocalParamOp>(op, newType, tagValue, tagName);
3244LogicalResult FIRRTLLowering::visitExpr(AggregateConstantOp op) {
3245 auto resultType =
lowerType(op.getResult().getType());
3247 getOrCreateAggregateConstantAttribute(op.getFieldsAttr(), resultType);
3249 return setLoweringTo<hw::AggregateConstantOp>(op, resultType,
3250 cast<ArrayAttr>(attr));
3253LogicalResult FIRRTLLowering::visitExpr(IsTagOp op) {
3255 auto tagName = op.getFieldNameAttr();
3256 auto lhs = getLoweredValue(op.getInput());
3257 if (isa<hw::StructType>(lhs.getType()))
3260 auto index = op.getFieldIndex();
3261 auto enumType = op.getInput().getType().base();
3262 auto tagValue = enumType.getElementValueAttr(index);
3263 auto tagValueType = IntegerType::get(op.getContext(), enumType.getTagWidth());
3264 auto loweredTagValue = IntegerAttr::get(tagValueType, tagValue.getValue());
3265 auto rhs = builder.create<sv::LocalParamOp>(op.getLoc(), tagValueType,
3266 loweredTagValue, tagName);
3268 Type resultType = builder.getIntegerType(1);
3269 return setLoweringTo<comb::ICmpOp>(op, resultType, ICmpPredicate::eq, lhs,
3273LogicalResult FIRRTLLowering::visitExpr(SubtagOp op) {
3276 return setLowering(op, Value());
3278 auto tagName = op.getFieldNameAttr();
3279 auto input = getLoweredValue(op.getInput());
3281 return setLoweringTo<hw::UnionExtractOp>(op, field, tagName);
3284LogicalResult FIRRTLLowering::visitExpr(TagExtractOp op) {
3287 return setLowering(op, Value());
3289 auto input = getLoweredValue(op.getInput());
3295 if (isa<hw::StructType>(input.getType())) {
3296 return setLoweringTo<hw::StructExtractOp>(op, input,
"tag");
3301 return setLowering(op, input);
3308LogicalResult FIRRTLLowering::visitDecl(WireOp op) {
3309 auto origResultType = op.getResult().getType();
3313 if (!type_isa<FIRRTLType>(origResultType)) {
3314 createBackedge(op.getResult(), origResultType);
3318 auto resultType =
lowerType(origResultType);
3322 if (resultType.isInteger(0)) {
3323 if (op.getInnerSym())
3324 return op.emitError(
"zero width wire is referenced by name [")
3325 << *op.getInnerSym() <<
"] (e.g. in an XMR) but must be removed";
3326 return setLowering(op.getResult(), Value());
3330 auto innerSym = lowerInnerSymbol(op);
3331 auto name = op.getNameAttr();
3334 auto wire = builder.create<hw::WireOp>(
3335 op.getLoc(), getOrCreateZConstant(resultType), name, innerSym);
3337 if (
auto svAttrs = sv::getSVAttributes(op))
3338 sv::setSVAttributes(wire, svAttrs);
3340 return setLowering(op.getResult(), wire);
3343LogicalResult FIRRTLLowering::visitDecl(VerbatimWireOp op) {
3344 auto resultTy =
lowerType(op.getType());
3347 resultTy = sv::InOutType::get(op.getContext(), resultTy);
3349 SmallVector<Value, 4> operands;
3350 operands.reserve(op.getSubstitutions().size());
3351 for (
auto operand : op.getSubstitutions()) {
3352 auto lowered = getLoweredValue(operand);
3355 operands.push_back(lowered);
3358 ArrayAttr symbols = op.getSymbolsAttr();
3360 symbols = ArrayAttr::get(op.getContext(), {});
3362 return setLoweringTo<sv::VerbatimExprSEOp>(op, resultTy, op.getTextAttr(),
3366LogicalResult FIRRTLLowering::visitDecl(NodeOp op) {
3367 auto operand = getLoweredValue(op.getInput());
3369 return handleZeroBit(op.getInput(), [&]() -> LogicalResult {
3370 if (op.getInnerSym())
3371 return op.emitError(
"zero width node is referenced by name [")
3372 << *op.getInnerSym()
3373 <<
"] (e.g. in an XMR) but must be "
3375 return setLowering(op.getResult(), Value());
3381 auto name = op.getNameAttr();
3382 auto innerSym = lowerInnerSymbol(op);
3385 operand = builder.create<hw::WireOp>(operand, name, innerSym);
3388 if (
auto svAttrs = sv::getSVAttributes(op)) {
3390 operand = builder.create<hw::WireOp>(operand, name);
3391 sv::setSVAttributes(operand.getDefiningOp(), svAttrs);
3394 return setLowering(op.getResult(), operand);
3397LogicalResult FIRRTLLowering::visitDecl(RegOp op) {
3398 auto resultType =
lowerType(op.getResult().getType());
3401 if (resultType.isInteger(0))
3402 return setLowering(op.getResult(), Value());
3404 Value clockVal = getLoweredValue(op.getClockVal());
3409 auto innerSym = lowerInnerSymbol(op);
3410 Backedge inputEdge = backedgeBuilder.
get(resultType);
3411 auto reg = builder.create<seq::FirRegOp>(inputEdge, clockVal,
3412 op.getNameAttr(), innerSym);
3415 if (
auto randomRegister = op->getAttr(
"firrtl.random_init_register"))
3416 reg->setAttr(
"firrtl.random_init_register", randomRegister);
3417 if (
auto randomStart = op->getAttr(
"firrtl.random_init_start"))
3418 reg->setAttr(
"firrtl.random_init_start", randomStart);
3419 if (
auto randomEnd = op->getAttr(
"firrtl.random_init_end"))
3420 reg->setAttr(
"firrtl.random_init_end", randomEnd);
3423 if (
auto svAttrs = sv::getSVAttributes(op))
3424 sv::setSVAttributes(reg, svAttrs);
3426 inputEdge.setValue(reg);
3427 (void)setLowering(op.getResult(),
reg);
3431LogicalResult FIRRTLLowering::visitDecl(RegResetOp op) {
3432 auto resultType =
lowerType(op.getResult().getType());
3435 if (resultType.isInteger(0))
3436 return setLowering(op.getResult(), Value());
3438 Value clockVal = getLoweredValue(op.getClockVal());
3439 Value resetSignal = getLoweredValue(op.getResetSignal());
3441 Value resetValue = getLoweredAndExtOrTruncValue(
3442 op.getResetValue(), type_cast<FIRRTLBaseType>(op.getResult().getType()));
3444 if (!clockVal || !resetSignal || !resetValue)
3448 auto innerSym = lowerInnerSymbol(op);
3449 bool isAsync = type_isa<AsyncResetType>(op.getResetSignal().getType());
3450 Backedge inputEdge = backedgeBuilder.
get(resultType);
3452 builder.create<seq::FirRegOp>(inputEdge, clockVal, op.getNameAttr(),
3453 resetSignal, resetValue, innerSym, isAsync);
3456 if (
auto randomRegister = op->getAttr(
"firrtl.random_init_register"))
3457 reg->setAttr(
"firrtl.random_init_register", randomRegister);
3458 if (
auto randomStart = op->getAttr(
"firrtl.random_init_start"))
3459 reg->setAttr(
"firrtl.random_init_start", randomStart);
3460 if (
auto randomEnd = op->getAttr(
"firrtl.random_init_end"))
3461 reg->setAttr(
"firrtl.random_init_end", randomEnd);
3464 if (
auto svAttrs = sv::getSVAttributes(op))
3465 sv::setSVAttributes(reg, svAttrs);
3467 inputEdge.setValue(reg);
3468 (void)setLowering(op.getResult(),
reg);
3473LogicalResult FIRRTLLowering::visitDecl(MemOp op) {
3476 if (type_isa<BundleType>(op.getDataType()))
3477 return op.emitOpError(
3478 "should have already been lowered from a ground type to an aggregate "
3479 "type using the LowerTypes pass. Use "
3480 "'firtool --lower-types' or 'circt-opt "
3481 "--pass-pipeline='firrtl.circuit(firrtl-lower-types)' "
3487 auto memType = seq::FirMemType::get(
3490 : std::optional<uint32_t>());
3492 seq::FirMemInitAttr memInit;
3493 if (
auto init = op.getInitAttr())
3494 memInit = seq::FirMemInitAttr::get(init.getContext(), init.getFilename(),
3495 init.getIsBinary(), init.getIsInline());
3497 auto memDecl = builder.create<seq::FirMemOp>(
3500 op.getInnerSymAttr(), memInit, op.getPrefixAttr(), Attribute{});
3504 if (!circuitState.isInDUT(theModule))
3505 if (
auto testBenchDir = circuitState.getTestBenchDirectory())
3506 memDecl.setOutputFileAttr(testBenchDir);
3510 for (
size_t i = 0, e = op.getNumResults(); i != e; ++i) {
3512 auto addOutput = [&](StringRef field,
size_t width, Value value) {
3515 (void)setLowering(a, value);
3521 auto addInput = [&](StringRef field, Value backedge) {
3523 if (cast<FIRRTLBaseType>(a.getType())
3525 .getBitWidthOrSentinel() > 0)
3526 (void)setLowering(a, backedge);
3532 auto addInputPort = [&](StringRef field,
size_t width) -> Value {
3536 Value backedge, portValue;
3538 portValue = getOrCreateXConstant(1);
3540 auto portType = IntegerType::get(op.getContext(), width);
3541 backedge = portValue = createBackedge(builder.getLoc(), portType);
3543 addInput(field, backedge);
3547 auto addClock = [&](StringRef field) -> Value {
3548 Type clockTy = seq::ClockType::get(op.getContext());
3549 Value portValue = createBackedge(builder.getLoc(), clockTy);
3550 addInput(field, portValue);
3554 auto memportKind = op.getPortKind(i);
3555 if (memportKind == MemOp::PortKind::Read) {
3556 auto addr = addInputPort(
"addr", op.getAddrBits());
3557 auto en = addInputPort(
"en", 1);
3558 auto clk = addClock(
"clk");
3559 auto data = builder.create<seq::FirMemReadOp>(memDecl,
addr,
clk,
en);
3561 }
else if (memportKind == MemOp::PortKind::ReadWrite) {
3562 auto addr = addInputPort(
"addr", op.getAddrBits());
3563 auto en = addInputPort(
"en", 1);
3564 auto clk = addClock(
"clk");
3567 auto mode = addInputPort(
"wmode", 1);
3569 mode = builder.createOrFold<
comb::AndOp>(mode, addInputPort(
"wmask", 1),
3576 auto rdata = builder.create<seq::FirMemReadWriteOp>(
3580 auto addr = addInputPort(
"addr", op.getAddrBits());
3583 auto en = addInputPort(
"en", 1);
3587 auto clk = addClock(
"clk");
3600LogicalResult FIRRTLLowering::visitDecl(InstanceOp oldInstance) {
3601 Operation *oldModule =
3602 oldInstance.getReferencedModule(circuitState.getInstanceGraph());
3604 auto *newModule = circuitState.getNewModule(oldModule);
3606 oldInstance->emitOpError(
"could not find module [")
3607 << oldInstance.getModuleName() <<
"] referenced by instance";
3613 ArrayAttr parameters;
3614 if (
auto oldExtModule = dyn_cast<FExtModuleOp>(oldModule))
3619 SmallVector<PortInfo, 8> portInfo = cast<FModuleLike>(oldModule).getPorts();
3624 for (
unsigned portIdx = 0, e = portInfo.size(); portIdx != e; ++portIdx)
3625 portIndicesByName[portInfo[portIdx].name] = portIdx;
3629 SmallVector<Value, 8> operands;
3630 for (
size_t portIndex = 0, e = portInfo.size(); portIndex != e; ++portIndex) {
3631 auto &port = portInfo[portIndex];
3634 oldInstance->emitOpError(
"could not lower type of port ") << port.name;
3639 if (portType.isInteger(0))
3643 if (port.isOutput())
3646 auto portResult = oldInstance.getResult(portIndex);
3647 assert(portResult &&
"invalid IR, couldn't find port");
3651 if (port.isInput()) {
3652 operands.push_back(createBackedge(portResult, portType));
3658 if (type_isa<AnalogType>(portResult.getType()) && portResult.hasOneUse()) {
3659 if (
auto attach = dyn_cast<AttachOp>(*portResult.getUsers().begin())) {
3661 auto loweredResult = getPossiblyInoutLoweredValue(source);
3662 operands.push_back(loweredResult);
3663 (void)setLowering(portResult, loweredResult);
3672 portType,
"." + port.getName().str() +
".wire");
3676 (void)setLowering(portResult, wire);
3678 operands.push_back(wire);
3685 auto innerSym = oldInstance.getInnerSymAttr();
3686 if (oldInstance.getLowerToBind()) {
3689 oldInstance.getContext(), oldInstance.getInnerSymAttr(), 0,
3692 auto bindOp = builder.create<sv::BindOp>(theModule.getNameAttr(),
3693 innerSym.getSymName());
3696 if (
auto outputFile = oldInstance->getAttr(
"output_file"))
3697 bindOp->setAttr(
"output_file", outputFile);
3700 circuitState.addBind(bindOp);
3704 auto newInstance = builder.create<hw::InstanceOp>(
3705 newModule, oldInstance.getNameAttr(), operands, parameters, innerSym);
3707 if (oldInstance.getLowerToBind() || oldInstance.getDoNotPrint())
3708 newInstance.setDoNotPrintAttr(builder.getUnitAttr());
3710 if (newInstance.getInnerSymAttr())
3711 if (
auto forceName = circuitState.instanceForceNames.lookup(
3712 {cast<hw::HWModuleOp>(newInstance->getParentOp()).getNameAttr(),
3713 newInstance.getInnerNameAttr()}))
3714 newInstance->setAttr(
"hw.verilogName", forceName);
3718 unsigned resultNo = 0;
3719 for (
size_t portIndex = 0, e = portInfo.size(); portIndex != e; ++portIndex) {
3720 auto &port = portInfo[portIndex];
3724 Value resultVal = newInstance.getResult(resultNo);
3726 auto oldPortResult = oldInstance.getResult(portIndex);
3727 (void)setLowering(oldPortResult, resultVal);
3733LogicalResult FIRRTLLowering::visitDecl(ContractOp oldOp) {
3734 SmallVector<Value> inputs;
3735 SmallVector<Type> types;
3736 for (
auto input : oldOp.getInputs()) {
3737 auto lowered = getLoweredValue(input);
3740 inputs.push_back(lowered);
3741 types.push_back(lowered.getType());
3744 auto newOp = builder.create<verif::ContractOp>(types, inputs);
3745 newOp->setDiscardableAttrs(oldOp->getDiscardableAttrDictionary());
3746 auto &body = newOp.getBody().emplaceBlock();
3748 for (
auto [newResult, oldResult, oldArg] :
3749 llvm::zip(newOp.getResults(), oldOp.getResults(),
3750 oldOp.getBody().getArguments())) {
3751 if (failed(setLowering(oldResult, newResult)))
3753 if (failed(setLowering(oldArg, newResult)))
3757 body.getOperations().splice(body.end(),
3758 oldOp.getBody().front().getOperations());
3759 addToWorklist(body);
3769LogicalResult FIRRTLLowering::lowerNoopCast(Operation *op) {
3770 auto operand = getPossiblyInoutLoweredValue(op->getOperand(0));
3775 return setLowering(op->getResult(0), operand);
3778LogicalResult FIRRTLLowering::visitExpr(AsSIntPrimOp op) {
3779 if (isa<ClockType>(op.getInput().getType()))
3780 return setLowering(op->getResult(0),
3781 getLoweredNonClockValue(op.getInput()));
3782 return lowerNoopCast(op);
3785LogicalResult FIRRTLLowering::visitExpr(AsUIntPrimOp op) {
3786 if (isa<ClockType>(op.getInput().getType()))
3787 return setLowering(op->getResult(0),
3788 getLoweredNonClockValue(op.getInput()));
3789 return lowerNoopCast(op);
3792LogicalResult FIRRTLLowering::visitExpr(AsClockPrimOp op) {
3793 return setLoweringTo<seq::ToClockOp>(op, getLoweredValue(op.getInput()));
3796LogicalResult FIRRTLLowering::visitUnrealizedConversionCast(
3797 mlir::UnrealizedConversionCastOp op) {
3799 if (op.getNumOperands() != 1 || op.getNumResults() != 1)
3802 auto operand = op.getOperand(0);
3803 auto result = op.getResult(0);
3806 if (type_isa<FIRRTLType>(operand.getType()) &&
3807 type_isa<FIRRTLType>(result.getType()))
3808 return lowerNoopCast(op);
3812 if (!type_isa<FIRRTLType>(operand.getType())) {
3813 if (type_isa<FIRRTLType>(result.getType()))
3814 return setLowering(result, getPossiblyInoutLoweredValue(operand));
3820 auto loweredResult = getLoweredValue(operand);
3821 if (!loweredResult) {
3824 if (operand.getType().isSignlessInteger(0)) {
3825 return setLowering(result, Value());
3832 result.replaceAllUsesWith(loweredResult);
3836LogicalResult FIRRTLLowering::visitExpr(HWStructCastOp op) {
3839 if (
auto opStructType = dyn_cast<hw::StructType>(op.getOperand().getType()))
3840 return setLowering(op, op.getOperand());
3844 auto result = getLoweredValue(op.getOperand());
3850 op.replaceAllUsesWith(result);
3854LogicalResult FIRRTLLowering::visitExpr(BitCastOp op) {
3855 auto operand = getLoweredValue(op.getOperand());
3858 auto resultType =
lowerType(op.getType());
3862 return setLoweringTo<hw::BitcastOp>(op, resultType, operand);
3865LogicalResult FIRRTLLowering::visitExpr(CvtPrimOp op) {
3866 auto operand = getLoweredValue(op.getOperand());
3870 if (type_cast<IntType>(op.getOperand().getType()).isUnsigned())
3871 return setLowering(op, getOrCreateIntConstant(1, 0));
3873 return setLowering(op, Value());
3878 if (type_cast<IntType>(op.getOperand().getType()).isSigned())
3879 return setLowering(op, operand);
3882 auto zero = getOrCreateIntConstant(1, 0);
3883 return setLoweringTo<comb::ConcatOp>(op, zero, operand);
3886LogicalResult FIRRTLLowering::visitExpr(NotPrimOp op) {
3887 auto operand = getLoweredValue(op.getInput());
3891 auto allOnes = getOrCreateIntConstant(
3892 APInt::getAllOnes(operand.getType().getIntOrFloatBitWidth()));
3893 return setLoweringTo<comb::XorOp>(op, operand, allOnes,
true);
3896LogicalResult FIRRTLLowering::visitExpr(NegPrimOp op) {
3899 auto operand = getLoweredAndExtendedValue(op.getInput(), op.getType());
3903 auto resultType =
lowerType(op.getType());
3905 auto zero = getOrCreateIntConstant(resultType.getIntOrFloatBitWidth(), 0);
3906 return setLoweringTo<comb::SubOp>(op, zero, operand,
true);
3910LogicalResult FIRRTLLowering::visitExpr(PadPrimOp op) {
3911 auto operand = getLoweredAndExtendedValue(op.getInput(), op.getType());
3914 return setLowering(op, operand);
3917LogicalResult FIRRTLLowering::visitExpr(XorRPrimOp op) {
3918 auto operand = getLoweredValue(op.getInput());
3921 return setLowering(op, getOrCreateIntConstant(1, 0));
3926 return setLoweringTo<comb::ParityOp>(op, builder.getIntegerType(1), operand,
3930LogicalResult FIRRTLLowering::visitExpr(AndRPrimOp op) {
3931 auto operand = getLoweredValue(op.getInput());
3934 return setLowering(op, getOrCreateIntConstant(1, 1));
3939 return setLoweringTo<comb::ICmpOp>(
3940 op, ICmpPredicate::eq, operand,
3941 getOrCreateIntConstant(
3942 APInt::getAllOnes(operand.getType().getIntOrFloatBitWidth())),
3946LogicalResult FIRRTLLowering::visitExpr(OrRPrimOp op) {
3947 auto operand = getLoweredValue(op.getInput());
3950 return setLowering(op, getOrCreateIntConstant(1, 0));
3956 return setLoweringTo<comb::ICmpOp>(
3957 op, ICmpPredicate::ne, operand,
3958 getOrCreateIntConstant(operand.getType().getIntOrFloatBitWidth(), 0),
3966template <
typename ResultOpType>
3967LogicalResult FIRRTLLowering::lowerBinOpToVariadic(Operation *op) {
3968 auto resultType = op->getResult(0).getType();
3969 auto lhs = getLoweredAndExtendedValue(op->getOperand(0), resultType);
3970 auto rhs = getLoweredAndExtendedValue(op->getOperand(1), resultType);
3974 return setLoweringTo<ResultOpType>(op, lhs, rhs,
true);
3980template <
typename ResultOpType>
3981LogicalResult FIRRTLLowering::lowerElementwiseLogicalOp(Operation *op) {
3982 auto resultType = op->getResult(0).getType();
3983 auto lhs = getLoweredAndExtendedValue(op->getOperand(0), resultType);
3984 auto rhs = getLoweredAndExtendedValue(op->getOperand(1), resultType);
3995 auto intType = builder.getIntegerType(*bitwidth);
3996 auto retType = lhs.getType();
3999 auto result = builder.createOrFold<ResultOpType>(lhs, rhs,
true);
4000 return setLoweringTo<hw::BitcastOp>(op, retType, result);
4005template <
typename ResultUn
signedOpType,
typename ResultSignedOpType>
4006LogicalResult FIRRTLLowering::lowerBinOp(Operation *op) {
4008 auto resultType = op->getResult(0).getType();
4009 auto lhs = getLoweredAndExtendedValue(op->getOperand(0), resultType);
4010 auto rhs = getLoweredAndExtendedValue(op->getOperand(1), resultType);
4015 if (type_cast<IntType>(resultType).isSigned())
4016 return setLoweringTo<ResultSignedOpType>(op, lhs, rhs,
true);
4017 return setLoweringTo<ResultUnsignedOpType>(op, lhs, rhs,
true);
4022LogicalResult FIRRTLLowering::lowerCmpOp(Operation *op, ICmpPredicate signedOp,
4023 ICmpPredicate unsignedOp) {
4025 auto lhsIntType = type_cast<IntType>(op->getOperand(0).getType());
4026 auto rhsIntType = type_cast<IntType>(op->getOperand(1).getType());
4027 if (!lhsIntType.hasWidth() || !rhsIntType.hasWidth())
4031 if (cmpType.getWidth() == 0)
4032 cmpType = UIntType::get(builder.getContext(), 1);
4033 auto lhs = getLoweredAndExtendedValue(op->getOperand(0), cmpType);
4034 auto rhs = getLoweredAndExtendedValue(op->getOperand(1), cmpType);
4039 Type resultType = builder.getIntegerType(1);
4040 return setLoweringTo<comb::ICmpOp>(
4041 op, resultType, lhsIntType.isSigned() ? signedOp : unsignedOp, lhs, rhs,
4047template <
typename SignedOp,
typename Un
signedOp>
4048LogicalResult FIRRTLLowering::lowerDivLikeOp(Operation *op) {
4052 auto opType = type_cast<IntType>(op->getResult(0).getType());
4053 if (opType.getWidth() == 0)
4054 return setLowering(op->getResult(0), Value());
4058 auto lhs = getLoweredAndExtendedValue(op->getOperand(0), resultType);
4059 auto rhs = getLoweredAndExtendedValue(op->getOperand(1), resultType);
4064 if (opType.isSigned())
4065 result = builder.createOrFold<SignedOp>(lhs, rhs,
true);
4067 result = builder.createOrFold<UnsignedOp>(lhs, rhs,
true);
4069 if (
auto *definingOp = result.getDefiningOp())
4072 if (resultType == opType)
4073 return setLowering(op->getResult(0), result);
4074 return setLoweringTo<comb::ExtractOp>(op,
lowerType(opType), result, 0);
4077LogicalResult FIRRTLLowering::visitExpr(CatPrimOp op) {
4079 if (op.getInputs().empty())
4080 return setLowering(op, Value());
4082 SmallVector<Value> loweredOperands;
4085 for (
auto operand : op.getInputs()) {
4086 auto loweredOperand = getLoweredValue(operand);
4087 if (loweredOperand) {
4088 loweredOperands.push_back(loweredOperand);
4091 auto result =
handleZeroBit(operand, [&]() {
return success(); });
4099 if (loweredOperands.empty())
4100 return setLowering(op, Value());
4103 return setLoweringTo<comb::ConcatOp>(op, loweredOperands);
4110LogicalResult FIRRTLLowering::visitExpr(IsXIntrinsicOp op) {
4111 auto input = getLoweredNonClockValue(op.getArg());
4115 if (!isa<IntType>(input.getType())) {
4116 auto srcType = op.getArg().getType();
4118 assert(bitwidth &&
"Unknown width");
4119 auto intType = builder.getIntegerType(*bitwidth);
4120 input = builder.createOrFold<
hw::BitcastOp>(intType, input);
4123 return setLoweringTo<comb::ICmpOp>(
4124 op, ICmpPredicate::ceq, input,
4125 getOrCreateXConstant(input.getType().getIntOrFloatBitWidth()),
true);
4128LogicalResult FIRRTLLowering::visitStmt(FPGAProbeIntrinsicOp op) {
4129 auto operand = getLoweredValue(op.getInput());
4130 builder.create<hw::WireOp>(operand);
4134LogicalResult FIRRTLLowering::visitExpr(PlusArgsTestIntrinsicOp op) {
4135 return setLoweringTo<sim::PlusArgsTestOp>(op, builder.getIntegerType(1),
4136 op.getFormatStringAttr());
4139LogicalResult FIRRTLLowering::visitExpr(PlusArgsValueIntrinsicOp op) {
4140 auto type =
lowerType(op.getResult().getType());
4144 auto valueOp = builder.create<sim::PlusArgsValueOp>(
4145 builder.getIntegerType(1), type, op.getFormatStringAttr());
4146 if (failed(setLowering(op.getResult(), valueOp.getResult())))
4148 if (failed(setLowering(op.getFound(), valueOp.getFound())))
4153LogicalResult FIRRTLLowering::visitExpr(SizeOfIntrinsicOp op) {
4154 op.emitError(
"SizeOf should have been resolved.");
4158LogicalResult FIRRTLLowering::visitExpr(ClockGateIntrinsicOp op) {
4160 if (op.getTestEnable())
4161 testEnable = getLoweredValue(op.getTestEnable());
4162 return setLoweringTo<seq::ClockGateOp>(
4163 op, getLoweredValue(op.getInput()), getLoweredValue(op.getEnable()),
4164 testEnable, hw::InnerSymAttr{});
4167LogicalResult FIRRTLLowering::visitExpr(ClockInverterIntrinsicOp op) {
4168 auto operand = getLoweredValue(op.getInput());
4169 return setLoweringTo<seq::ClockInverterOp>(op, operand);
4172LogicalResult FIRRTLLowering::visitExpr(ClockDividerIntrinsicOp op) {
4173 auto operand = getLoweredValue(op.getInput());
4174 return setLoweringTo<seq::ClockDividerOp>(op, operand, op.getPow2());
4177LogicalResult FIRRTLLowering::visitExpr(LTLAndIntrinsicOp op) {
4178 return setLoweringToLTL<ltl::AndOp>(
4180 ValueRange{getLoweredValue(op.getLhs()), getLoweredValue(op.getRhs())});
4183LogicalResult FIRRTLLowering::visitExpr(LTLOrIntrinsicOp op) {
4184 return setLoweringToLTL<ltl::OrOp>(
4186 ValueRange{getLoweredValue(op.getLhs()), getLoweredValue(op.getRhs())});
4189LogicalResult FIRRTLLowering::visitExpr(LTLIntersectIntrinsicOp op) {
4190 return setLoweringToLTL<ltl::IntersectOp>(
4192 ValueRange{getLoweredValue(op.getLhs()), getLoweredValue(op.getRhs())});
4195LogicalResult FIRRTLLowering::visitExpr(LTLDelayIntrinsicOp op) {
4196 return setLoweringToLTL<ltl::DelayOp>(op, getLoweredValue(op.getInput()),
4197 op.getDelayAttr(), op.getLengthAttr());
4200LogicalResult FIRRTLLowering::visitExpr(LTLConcatIntrinsicOp op) {
4201 return setLoweringToLTL<ltl::ConcatOp>(
4203 ValueRange{getLoweredValue(op.getLhs()), getLoweredValue(op.getRhs())});
4206LogicalResult FIRRTLLowering::visitExpr(LTLRepeatIntrinsicOp op) {
4207 return setLoweringToLTL<ltl::RepeatOp>(op, getLoweredValue(op.getInput()),
4208 op.getBaseAttr(), op.getMoreAttr());
4211LogicalResult FIRRTLLowering::visitExpr(LTLGoToRepeatIntrinsicOp op) {
4212 return setLoweringToLTL<ltl::GoToRepeatOp>(
4213 op, getLoweredValue(op.getInput()), op.getBaseAttr(), op.getMoreAttr());
4216LogicalResult FIRRTLLowering::visitExpr(LTLNonConsecutiveRepeatIntrinsicOp op) {
4217 return setLoweringToLTL<ltl::NonConsecutiveRepeatOp>(
4218 op, getLoweredValue(op.getInput()), op.getBaseAttr(), op.getMoreAttr());
4221LogicalResult FIRRTLLowering::visitExpr(LTLNotIntrinsicOp op) {
4222 return setLoweringToLTL<ltl::NotOp>(op, getLoweredValue(op.getInput()));
4225LogicalResult FIRRTLLowering::visitExpr(LTLImplicationIntrinsicOp op) {
4226 return setLoweringToLTL<ltl::ImplicationOp>(
4228 ValueRange{getLoweredValue(op.getLhs()), getLoweredValue(op.getRhs())});
4231LogicalResult FIRRTLLowering::visitExpr(LTLUntilIntrinsicOp op) {
4232 return setLoweringToLTL<ltl::UntilOp>(
4234 ValueRange{getLoweredValue(op.getLhs()), getLoweredValue(op.getRhs())});
4237LogicalResult FIRRTLLowering::visitExpr(LTLEventuallyIntrinsicOp op) {
4238 return setLoweringToLTL<ltl::EventuallyOp>(op,
4239 getLoweredValue(op.getInput()));
4242LogicalResult FIRRTLLowering::visitExpr(LTLClockIntrinsicOp op) {
4243 return setLoweringToLTL<ltl::ClockOp>(op, getLoweredValue(op.getInput()),
4244 ltl::ClockEdge::Pos,
4245 getLoweredNonClockValue(op.getClock()));
4248template <
typename TargetOp,
typename IntrinsicOp>
4249LogicalResult FIRRTLLowering::lowerVerifIntrinsicOp(IntrinsicOp op) {
4250 auto property = getLoweredValue(op.getProperty());
4251 auto enable = op.getEnable() ? getLoweredValue(op.getEnable()) : Value();
4252 builder.create<TargetOp>(property, enable, op.getLabelAttr());
4256LogicalResult FIRRTLLowering::visitStmt(VerifAssertIntrinsicOp op) {
4257 return lowerVerifIntrinsicOp<verif::AssertOp>(op);
4260LogicalResult FIRRTLLowering::visitStmt(VerifAssumeIntrinsicOp op) {
4261 return lowerVerifIntrinsicOp<verif::AssumeOp>(op);
4264LogicalResult FIRRTLLowering::visitStmt(VerifCoverIntrinsicOp op) {
4265 return lowerVerifIntrinsicOp<verif::CoverOp>(op);
4268LogicalResult FIRRTLLowering::visitStmt(VerifRequireIntrinsicOp op) {
4269 if (!isa<verif::ContractOp>(op->getParentOp()))
4270 return lowerVerifIntrinsicOp<verif::AssertOp>(op);
4271 return lowerVerifIntrinsicOp<verif::RequireOp>(op);
4274LogicalResult FIRRTLLowering::visitStmt(VerifEnsureIntrinsicOp op) {
4275 if (!isa<verif::ContractOp>(op->getParentOp()))
4276 return lowerVerifIntrinsicOp<verif::AssertOp>(op);
4277 return lowerVerifIntrinsicOp<verif::EnsureOp>(op);
4280LogicalResult FIRRTLLowering::visitExpr(HasBeenResetIntrinsicOp op) {
4281 auto clock = getLoweredNonClockValue(op.getClock());
4282 auto reset = getLoweredValue(op.getReset());
4283 if (!clock || !reset)
4285 auto resetType = op.getReset().getType();
4286 auto uintResetType = dyn_cast<UIntType>(resetType);
4287 auto isSync = uintResetType && uintResetType.getWidth() == 1;
4288 auto isAsync = isa<AsyncResetType>(resetType);
4289 if (!isAsync && !isSync) {
4290 auto d = op.emitError(
"uninferred reset passed to 'has_been_reset'; "
4291 "requires sync or async reset");
4292 d.attachNote() <<
"reset is of type " << resetType
4293 <<
", should be '!firrtl.uint<1>' or '!firrtl.asyncreset'";
4296 return setLoweringTo<verif::HasBeenResetOp>(op, clock, reset, isAsync);
4303LogicalResult FIRRTLLowering::visitExpr(BitsPrimOp op) {
4304 auto input = getLoweredValue(op.getInput());
4308 Type resultType = builder.getIntegerType(op.getHi() - op.getLo() + 1);
4309 return setLoweringTo<comb::ExtractOp>(op, resultType, input, op.getLo());
4312LogicalResult FIRRTLLowering::visitExpr(InvalidValueOp op) {
4313 auto resultTy =
lowerType(op.getType());
4320 if (type_isa<AnalogType>(op.getType()))
4323 return setLoweringTo<sv::WireOp>(op, resultTy,
".invalid_analog");
4326 if (type_cast<FIRRTLBaseType>(op.getType()).containsAnalog())
4337 auto constant = getOrCreateIntConstant(*bitwidth, 0);
4339 if (!type_isa<IntegerType>(resultTy))
4340 constant = builder.create<
hw::BitcastOp>(resultTy, constant);
4341 return setLowering(op, constant);
4345 op.emitOpError(
"unsupported type");
4349LogicalResult FIRRTLLowering::visitExpr(HeadPrimOp op) {
4350 auto input = getLoweredValue(op.getInput());
4353 auto inWidth = type_cast<IntegerType>(input.getType()).getWidth();
4354 if (op.getAmount() == 0)
4355 return setLowering(op, Value());
4356 Type resultType = builder.getIntegerType(op.getAmount());
4357 return setLoweringTo<comb::ExtractOp>(op, resultType, input,
4358 inWidth - op.getAmount());
4361LogicalResult FIRRTLLowering::visitExpr(ShlPrimOp op) {
4362 auto input = getLoweredValue(op.getInput());
4365 if (op.getAmount() == 0)
4367 return setLowering(op, getOrCreateIntConstant(op.getAmount(), 0));
4372 if (op.getAmount() == 0)
4373 return setLowering(op, input);
4375 auto zero = getOrCreateIntConstant(op.getAmount(), 0);
4376 return setLoweringTo<comb::ConcatOp>(op, input, zero);
4379LogicalResult FIRRTLLowering::visitExpr(ShrPrimOp op) {
4380 auto input = getLoweredValue(op.getInput());
4385 auto inWidth = type_cast<IntegerType>(input.getType()).getWidth();
4386 auto shiftAmount = op.getAmount();
4387 if (shiftAmount >= inWidth) {
4389 if (type_cast<IntType>(op.getInput().getType()).isUnsigned())
4390 return setLowering(op, {});
4393 shiftAmount = inWidth - 1;
4396 Type resultType = builder.getIntegerType(inWidth - shiftAmount);
4397 return setLoweringTo<comb::ExtractOp>(op, resultType, input, shiftAmount);
4400LogicalResult FIRRTLLowering::visitExpr(TailPrimOp op) {
4401 auto input = getLoweredValue(op.getInput());
4405 auto inWidth = type_cast<IntegerType>(input.getType()).getWidth();
4406 if (inWidth == op.getAmount())
4407 return setLowering(op, Value());
4408 Type resultType = builder.getIntegerType(inWidth - op.getAmount());
4409 return setLoweringTo<comb::ExtractOp>(op, resultType, input, 0);
4412LogicalResult FIRRTLLowering::visitExpr(MuxPrimOp op) {
4413 auto cond = getLoweredValue(op.getSel());
4414 auto ifTrue = getLoweredAndExtendedValue(op.getHigh(), op.getType());
4415 auto ifFalse = getLoweredAndExtendedValue(op.getLow(), op.getType());
4416 if (!cond || !ifTrue || !ifFalse)
4419 if (isa<ClockType>(op.getType()))
4420 return setLoweringTo<seq::ClockMuxOp>(op, cond, ifTrue, ifFalse);
4421 return setLoweringTo<comb::MuxOp>(op, ifTrue.getType(), cond, ifTrue, ifFalse,
4425LogicalResult FIRRTLLowering::visitExpr(Mux2CellIntrinsicOp op) {
4426 auto cond = getLoweredValue(op.getSel());
4427 auto ifTrue = getLoweredAndExtendedValue(op.getHigh(), op.getType());
4428 auto ifFalse = getLoweredAndExtendedValue(op.getLow(), op.getType());
4429 if (!cond || !ifTrue || !ifFalse)
4432 auto val = builder.create<
comb::MuxOp>(ifTrue.getType(), cond, ifTrue,
4434 return setLowering(op, createValueWithMuxAnnotation(val,
true));
4437LogicalResult FIRRTLLowering::visitExpr(Mux4CellIntrinsicOp op) {
4438 auto sel = getLoweredValue(op.getSel());
4439 auto v3 = getLoweredAndExtendedValue(op.getV3(), op.getType());
4440 auto v2 = getLoweredAndExtendedValue(op.getV2(), op.getType());
4441 auto v1 = getLoweredAndExtendedValue(op.getV1(), op.getType());
4442 auto v0 = getLoweredAndExtendedValue(op.getV0(), op.getType());
4443 if (!sel || !v3 || !v2 || !v1 || !v0)
4445 Value array[] = {v3, v2, v1, v0};
4448 return setLowering(op, createValueWithMuxAnnotation(val,
false));
4467Value FIRRTLLowering::createValueWithMuxAnnotation(Operation *op,
bool isMux2) {
4468 assert(op->getNumResults() == 1 &&
"only expect a single result");
4469 auto val = op->getResult(0);
4473 op, sv::SVAttributeAttr::get(builder.getContext(),
"cadence map_to_mux",
4480 OpBuilder::InsertionGuard guard(builder);
4481 builder.setInsertionPoint(op);
4482 StringRef namehint = isMux2 ?
"mux2cell_in" :
"mux4cell_in";
4483 for (
auto [idx, operand] :
llvm::enumerate(op->getOperands())) {
4485 op->getContext(),
nullptr, 0,
4488 builder.create<hw::WireOp>(operand, namehint + Twine(idx), innerSym);
4489 op->setOperand(idx, wire);
4493 auto assignOp = builder.create<
sv::AssignOp>(valWire, val);
4494 sv::setSVAttributes(assignOp,
4495 sv::SVAttributeAttr::get(builder.getContext(),
4496 "synopsys infer_mux_override",
4501Value FIRRTLLowering::createArrayIndexing(Value array, Value index) {
4503 auto size = hw::type_cast<hw::ArrayType>(array.getType()).getNumElements();
4508 if (!llvm::isPowerOf2_64(size)) {
4509 auto extElem = getOrCreateIntConstant(APInt(llvm::Log2_64_Ceil(size), 0));
4511 SmallVector<Value> temp(llvm::NextPowerOf2(size) - size, extValue);
4513 Value temp2[] = {ext.getResult(), array};
4519 return inBoundsRead;
4522LogicalResult FIRRTLLowering::visitExpr(MultibitMuxOp op) {
4524 auto index = getLoweredAndExtOrTruncValue(
4526 UIntType::get(op.getContext(),
4531 SmallVector<Value> loweredInputs;
4532 loweredInputs.reserve(op.getInputs().size());
4533 for (
auto input : op.getInputs()) {
4534 auto lowered = getLoweredAndExtendedValue(input, op.getType());
4537 loweredInputs.push_back(lowered);
4541 return setLowering(op, createArrayIndexing(array, index));
4544LogicalResult FIRRTLLowering::visitExpr(VerbatimExprOp op) {
4545 auto resultTy =
lowerType(op.getType());
4549 SmallVector<Value, 4> operands;
4550 operands.reserve(op.getSubstitutions().size());
4551 for (
auto operand : op.getSubstitutions()) {
4552 auto lowered = getLoweredValue(operand);
4555 operands.push_back(lowered);
4558 ArrayAttr symbols = op.getSymbolsAttr();
4560 symbols = ArrayAttr::get(op.getContext(), {});
4562 return setLoweringTo<sv::VerbatimExprOp>(op, resultTy, op.getTextAttr(),
4566LogicalResult FIRRTLLowering::visitExpr(XMRRefOp op) {
4570 Type baseType = op.getType().getType();
4573 if (isa<ClockType>(baseType))
4574 xmrType = builder.getIntegerType(1);
4578 return setLoweringTo<sv::XMRRefOp>(op, sv::InOutType::get(xmrType),
4579 op.getRef(), op.getVerbatimSuffixAttr());
4582LogicalResult FIRRTLLowering::visitExpr(XMRDerefOp op) {
4586 if (isa<ClockType>(op.getType()))
4587 xmrType = builder.getIntegerType(1);
4591 auto xmr = builder.create<sv::XMRRefOp>(
4592 sv::InOutType::get(xmrType), op.getRef(), op.getVerbatimSuffixAttr());
4593 auto readXmr = getReadValue(xmr);
4594 if (!isa<ClockType>(op.getType()))
4595 return setLowering(op, readXmr);
4596 return setLoweringTo<seq::ToClockOp>(op, readXmr);
4601LogicalResult FIRRTLLowering::visitExpr(TimeOp op) {
return success(); }
4602LogicalResult FIRRTLLowering::visitExpr(HierarchicalModuleNameOp op) {
4610LogicalResult FIRRTLLowering::visitStmt(SkipOp op) {
4622FailureOr<bool> FIRRTLLowering::lowerConnect(Value destVal, Value srcVal) {
4623 auto srcType = srcVal.getType();
4624 auto dstType = destVal.getType();
4625 if (srcType != dstType &&
4626 (isa<hw::TypeAliasType>(srcType) || isa<hw::TypeAliasType>(dstType))) {
4627 srcVal = builder.create<
hw::BitcastOp>(destVal.getType(), srcVal);
4629 return TypeSwitch<Operation *, FailureOr<bool>>(destVal.getDefiningOp())
4630 .Case<hw::WireOp>([&](
auto op) {
4631 maybeUnused(op.getInput());
4632 op.getInputMutable().assign(srcVal);
4635 .Case<seq::FirRegOp>([&](
auto op) {
4636 maybeUnused(op.getNext());
4637 op.getNextMutable().assign(srcVal);
4640 .Case<hw::StructExtractOp, hw::ArrayGetOp>([](
auto op) {
4643 op.emitOpError(
"used as connect destination");
4646 .Default([](
auto) {
return false; });
4649LogicalResult FIRRTLLowering::visitStmt(ConnectOp op) {
4650 auto dest = op.getDest();
4652 auto destType = type_cast<FIRRTLBaseType>(dest.getType()).getPassiveType();
4653 auto srcVal = getLoweredAndExtendedValue(op.getSrc(), destType);
4655 return handleZeroBit(op.getSrc(), []() { return success(); });
4657 auto destVal = getPossiblyInoutLoweredValue(dest);
4661 auto result = lowerConnect(destVal, srcVal);
4669 if (updateIfBackedge(destVal, srcVal))
4672 if (!isa<hw::InOutType>(destVal.getType()))
4673 return op.emitError(
"destination isn't an inout type");
4679LogicalResult FIRRTLLowering::visitStmt(MatchingConnectOp op) {
4680 auto dest = op.getDest();
4681 auto srcVal = getLoweredValue(op.getSrc());
4683 return handleZeroBit(op.getSrc(), []() { return success(); });
4685 auto destVal = getPossiblyInoutLoweredValue(dest);
4689 auto result = lowerConnect(destVal, srcVal);
4697 if (updateIfBackedge(destVal, srcVal))
4700 if (!isa<hw::InOutType>(destVal.getType()))
4701 return op.emitError(
"destination isn't an inout type");
4707LogicalResult FIRRTLLowering::visitStmt(ForceOp op) {
4708 auto srcVal = getLoweredValue(op.getSrc());
4712 auto destVal = getPossiblyInoutLoweredValue(op.getDest());
4716 if (!isa<hw::InOutType>(destVal.getType()))
4717 return op.emitError(
"destination isn't an inout type");
4720 circuitState.addMacroDecl(builder.getStringAttr(
"SYNTHESIS"));
4721 addToIfDefBlock(
"SYNTHESIS", std::function<
void()>(), [&]() {
4722 addToInitialBlock([&]() { builder.create<sv::ForceOp>(destVal, srcVal); });
4727LogicalResult FIRRTLLowering::visitStmt(RefForceOp op) {
4728 auto src = getLoweredNonClockValue(op.getSrc());
4729 auto clock = getLoweredNonClockValue(op.getClock());
4730 auto pred = getLoweredValue(op.getPredicate());
4731 if (!src || !clock || !pred)
4734 auto destVal = getPossiblyInoutLoweredValue(op.getDest());
4739 circuitState.addMacroDecl(builder.getStringAttr(
"SYNTHESIS"));
4740 addToIfDefBlock(
"SYNTHESIS", std::function<
void()>(), [&]() {
4741 addToAlwaysBlock(clock, [&]() {
4742 addIfProceduralBlock(
4743 pred, [&]() { builder.create<sv::ForceOp>(destVal, src); });
4748LogicalResult FIRRTLLowering::visitStmt(RefForceInitialOp op) {
4749 auto src = getLoweredNonClockValue(op.getSrc());
4750 auto pred = getLoweredValue(op.getPredicate());
4754 auto destVal = getPossiblyInoutLoweredValue(op.getDest());
4759 circuitState.addMacroDecl(builder.getStringAttr(
"SYNTHESIS"));
4760 addToIfDefBlock(
"SYNTHESIS", std::function<
void()>(), [&]() {
4761 addToInitialBlock([&]() {
4762 addIfProceduralBlock(
4763 pred, [&]() { builder.create<sv::ForceOp>(destVal, src); });
4768LogicalResult FIRRTLLowering::visitStmt(RefReleaseOp op) {
4769 auto clock = getLoweredNonClockValue(op.getClock());
4770 auto pred = getLoweredValue(op.getPredicate());
4771 if (!clock || !pred)
4774 auto destVal = getPossiblyInoutLoweredValue(op.getDest());
4779 circuitState.addMacroDecl(builder.getStringAttr(
"SYNTHESIS"));
4780 addToIfDefBlock(
"SYNTHESIS", std::function<
void()>(), [&]() {
4781 addToAlwaysBlock(clock, [&]() {
4782 addIfProceduralBlock(pred,
4783 [&]() { builder.create<sv::ReleaseOp>(destVal); });
4788LogicalResult FIRRTLLowering::visitStmt(RefReleaseInitialOp op) {
4789 auto destVal = getPossiblyInoutLoweredValue(op.getDest());
4790 auto pred = getLoweredValue(op.getPredicate());
4791 if (!destVal || !pred)
4795 circuitState.addMacroDecl(builder.getStringAttr(
"SYNTHESIS"));
4796 addToIfDefBlock(
"SYNTHESIS", std::function<
void()>(), [&]() {
4797 addToInitialBlock([&]() {
4798 addIfProceduralBlock(pred,
4799 [&]() { builder.create<sv::ReleaseOp>(destVal); });
4807 StringRef originalFormatString,
4808 mlir::OperandRange operands,
4809 StringAttr &result) {
4812 SmallString<32> formatString;
4813 for (
size_t i = 0, e = originalFormatString.size(), subIdx = 0; i != e; ++i) {
4814 char c = originalFormatString[i];
4818 formatString.push_back(c);
4821 SmallString<6> width;
4822 c = originalFormatString[++i];
4825 c = originalFormatString[++i];
4836 formatString.append(width);
4842 formatString.push_back(c);
4849 if (originalFormatString.slice(i, i + 4) !=
"{{}}") {
4850 formatString.push_back(c);
4854 auto substitution = operands[subIdx++];
4855 assert(type_isa<FStringType>(substitution.getType()) &&
4856 "the operand for a '{{}}' substitution must be an 'fstring' type");
4858 TypeSwitch<Operation *, LogicalResult>(substitution.getDefiningOp())
4859 .template Case<TimeOp>([&](
auto) {
4860 formatString.append(
"%0t");
4863 .
template Case<HierarchicalModuleNameOp>([&](
auto) {
4864 formatString.append(
"%m");
4867 .Default([&](
auto) {
4868 emitError(loc,
"has a substitution with an unimplemented "
4870 .attachNote(substitution.getLoc())
4871 <<
"op with an unimplemented lowering is here";
4881 formatString.push_back(c);
4885 result = StringAttr::get(loc->getContext(), formatString);
4892LogicalResult FIRRTLLowering::visitPrintfLike(
4893 T op,
const FileDescriptorInfo &fileDescriptorInfo,
bool usePrintfCond) {
4894 auto clock = getLoweredNonClockValue(op.getClock());
4895 auto cond = getLoweredValue(op.getCond());
4896 if (!clock || !cond)
4899 StringAttr formatString;
4901 op.getSubstitutions(), formatString)))
4904 auto fn = [&](Value fd) {
4905 SmallVector<Value> operands;
4906 if (failed(loweredFmtOperands(op.getSubstitutions(), operands)))
4908 builder.create<sv::FWriteOp>(op.getLoc(), fd, formatString, operands);
4912 return lowerStatementWithFd(fileDescriptorInfo, clock, cond, fn,
4916LogicalResult FIRRTLLowering::visitStmt(FPrintFOp op) {
4917 StringAttr outputFileAttr;
4919 op.getOutputFileSubstitutions(),
4923 FileDescriptorInfo outputFile(outputFileAttr,
4924 op.getOutputFileSubstitutions());
4925 return visitPrintfLike(op, outputFile,
false);
4929LogicalResult FIRRTLLowering::visitStmt(FFlushOp op) {
4930 auto clock = getLoweredNonClockValue(op.getClock());
4931 auto cond = getLoweredValue(op.getCond());
4932 if (!clock || !cond)
4935 auto fn = [&](Value fd) {
4936 builder.create<sv::FFlushOp>(op.getLoc(), fd);
4940 if (!op.getOutputFileAttr())
4941 return lowerStatementWithFd({}, clock, cond, fn,
false);
4945 StringAttr outputFileAttr;
4947 op.getOutputFileSubstitutions(),
4951 return lowerStatementWithFd(
4952 FileDescriptorInfo(outputFileAttr, op.getOutputFileSubstitutions()),
4953 clock, cond, fn,
false);
4958LogicalResult FIRRTLLowering::visitStmt(StopOp op) {
4959 auto clock = getLoweredValue(op.getClock());
4960 auto cond = getLoweredValue(op.getCond());
4961 if (!clock || !cond)
4964 circuitState.usedStopCond =
true;
4965 circuitState.addFragment(theModule,
"STOP_COND_FRAGMENT");
4968 builder.create<sv::MacroRefExprOp>(cond.getType(),
"STOP_COND_");
4969 Value exitCond = builder.createOrFold<
comb::AndOp>(stopCond, cond,
true);
4971 if (op.getExitCode())
4972 builder.create<sim::FatalOp>(clock, exitCond);
4974 builder.create<sim::FinishOp>(clock, exitCond);
4982template <
typename... Args>
4984 StringRef opName, Args &&...args) {
4985 if (opName ==
"assert")
4986 return builder.create<sv::AssertOp>(std::forward<Args>(args)...);
4987 if (opName ==
"assume")
4988 return builder.create<sv::AssumeOp>(std::forward<Args>(args)...);
4989 if (opName ==
"cover")
4990 return builder.create<sv::CoverOp>(std::forward<Args>(args)...);
4991 llvm_unreachable(
"unknown verification op");
4997template <
typename... Args>
4999 StringRef opName, Args &&...args) {
5000 if (opName ==
"assert")
5001 return builder.create<sv::AssertConcurrentOp>(std::forward<Args>(args)...);
5002 if (opName ==
"assume")
5003 return builder.create<sv::AssumeConcurrentOp>(std::forward<Args>(args)...);
5004 if (opName ==
"cover")
5005 return builder.create<sv::CoverConcurrentOp>(std::forward<Args>(args)...);
5006 llvm_unreachable(
"unknown verification op");
5027LogicalResult FIRRTLLowering::lowerVerificationStatement(
5028 Operation *op, StringRef labelPrefix, Value opClock, Value opPredicate,
5029 Value opEnable, StringAttr opMessageAttr, ValueRange opOperands,
5030 StringAttr opNameAttr,
bool isConcurrent, EventControl opEventControl) {
5031 StringRef opName = op->getName().stripDialect();
5034 ArrayRef<Attribute> guards{};
5035 if (
auto guardsAttr = op->template getAttrOfType<ArrayAttr>(
"guards"))
5036 guards = guardsAttr.getValue();
5038 auto isCover = isa<CoverOp>(op);
5039 auto clock = getLoweredNonClockValue(opClock);
5040 auto enable = getLoweredValue(opEnable);
5041 auto predicate = getLoweredValue(opPredicate);
5042 if (!clock || !enable || !predicate)
5046 if (opNameAttr && !opNameAttr.getValue().empty())
5048 StringAttr prefixedLabel;
5051 StringAttr::get(builder.getContext(), labelPrefix + label.getValue());
5054 SmallVector<Value> messageOps;
5058 if (flavor == VerificationFlavor::IfElseFatal && !isa<AssertOp>(op))
5059 flavor = VerificationFlavor::None;
5061 if (flavor == VerificationFlavor::None) {
5065 auto format = op->getAttrOfType<StringAttr>(
"format");
5067 if (isConcurrent && format && format.getValue() ==
"ifElseFatal") {
5068 if (!isa<AssertOp>(op))
5069 return op->emitError()
5070 <<
"ifElseFatal format cannot be used for non-assertions";
5071 flavor = VerificationFlavor::IfElseFatal;
5072 }
else if (isConcurrent)
5073 flavor = VerificationFlavor::SVA;
5075 flavor = VerificationFlavor::Immediate;
5078 if (!isCover && opMessageAttr && !opMessageAttr.getValue().empty()) {
5079 message = opMessageAttr;
5080 if (failed(loweredFmtOperands(opOperands, messageOps)))
5083 if (flavor == VerificationFlavor::SVA) {
5088 for (
auto &loweredValue : messageOps)
5089 loweredValue = builder.create<
sv::SampledOp>(loweredValue);
5095 case VerificationFlavor::Immediate: {
5097 auto deferImmediate = circt::sv::DeferAssertAttr::get(
5098 builder.getContext(), circt::sv::DeferAssert::Immediate);
5099 addToAlwaysBlock(clock, [&]() {
5100 addIfProceduralBlock(enable, [&]() {
5102 prefixedLabel, message, messageOps);
5107 case VerificationFlavor::IfElseFatal: {
5108 assert(isa<AssertOp>(op) &&
"only assert is expected");
5111 auto boolType = IntegerType::get(builder.getContext(), 1);
5112 predicate = comb::createOrFoldNot(predicate, builder,
true);
5113 predicate = builder.createOrFold<
comb::AndOp>(enable, predicate,
true);
5115 circuitState.addMacroDecl(builder.getStringAttr(
"SYNTHESIS"));
5116 addToIfDefBlock(
"SYNTHESIS", {}, [&]() {
5117 addToAlwaysBlock(clock, [&]() {
5118 addIfProceduralBlock(predicate, [&]() {
5119 circuitState.usedStopCond =
true;
5120 circuitState.addFragment(theModule,
"STOP_COND_FRAGMENT");
5122 circuitState.usedAssertVerboseCond =
true;
5123 circuitState.addFragment(theModule,
"ASSERT_VERBOSE_COND_FRAGMENT");
5125 addIfProceduralBlock(
5126 builder.create<sv::MacroRefExprOp>(boolType,
5127 "ASSERT_VERBOSE_COND_"),
5128 [&]() { builder.create<sv::ErrorOp>(message, messageOps); });
5129 addIfProceduralBlock(
5130 builder.create<sv::MacroRefExprOp>(boolType,
"STOP_COND_"),
5131 [&]() { builder.create<sv::FatalOp>(); });
5137 case VerificationFlavor::SVA: {
5142 comb::createOrFoldNot(enable, builder,
true);
5144 builder.createOrFold<
comb::OrOp>(notEnable, predicate,
true);
5146 predicate = builder.createOrFold<
comb::AndOp>(enable, predicate,
true);
5150 sv::EventControl event;
5151 switch (opEventControl) {
5152 case EventControl::AtPosEdge:
5153 event = circt::sv::EventControl::AtPosEdge;
5155 case EventControl::AtEdge:
5156 event = circt::sv::EventControl::AtEdge;
5158 case EventControl::AtNegEdge:
5159 event = circt::sv::EventControl::AtNegEdge;
5165 circt::sv::EventControlAttr::get(builder.getContext(), event), clock,
5166 predicate, prefixedLabel, message, messageOps);
5169 case VerificationFlavor::None:
5171 "flavor `None` must be converted into one of concreate flavors");
5178 return emitGuards(op->getLoc(), guards,
emit);
5182LogicalResult FIRRTLLowering::visitStmt(AssertOp op) {
5183 return lowerVerificationStatement(
5184 op,
"assert__", op.getClock(), op.getPredicate(), op.getEnable(),
5185 op.getMessageAttr(), op.getSubstitutions(), op.getNameAttr(),
5186 op.getIsConcurrent(), op.getEventControl());
5190LogicalResult FIRRTLLowering::visitStmt(AssumeOp op) {
5191 return lowerVerificationStatement(
5192 op,
"assume__", op.getClock(), op.getPredicate(), op.getEnable(),
5193 op.getMessageAttr(), op.getSubstitutions(), op.getNameAttr(),
5194 op.getIsConcurrent(), op.getEventControl());
5198LogicalResult FIRRTLLowering::visitStmt(CoverOp op) {
5199 return lowerVerificationStatement(
5200 op,
"cover__", op.getClock(), op.getPredicate(), op.getEnable(),
5201 op.getMessageAttr(), op.getSubstitutions(), op.getNameAttr(),
5202 op.getIsConcurrent(), op.getEventControl());
5206LogicalResult FIRRTLLowering::visitStmt(UnclockedAssumeIntrinsicOp op) {
5211 auto guardsAttr = op->getAttrOfType<mlir::ArrayAttr>(
"guards");
5212 ArrayRef<Attribute> guards =
5213 guardsAttr ? guardsAttr.getValue() : ArrayRef<Attribute>();
5215 auto label = op.getNameAttr();
5216 StringAttr assumeLabel;
5217 if (label && !label.empty())
5219 StringAttr::get(builder.getContext(),
"assume__" + label.getValue());
5220 auto predicate = getLoweredValue(op.getPredicate());
5221 auto enable = getLoweredValue(op.getEnable());
5222 auto notEnable = comb::createOrFoldNot(enable, builder,
true);
5223 predicate = builder.createOrFold<
comb::OrOp>(notEnable, predicate,
true);
5225 SmallVector<Value> messageOps;
5226 for (
auto operand : op.getSubstitutions()) {
5227 auto loweredValue = getLoweredValue(operand);
5228 if (!loweredValue) {
5232 loweredValue = getOrCreateIntConstant(1, 0);
5234 messageOps.push_back(loweredValue);
5236 return emitGuards(op.getLoc(), guards, [&]() {
5237 builder.create<sv::AlwaysOp>(
5238 ArrayRef(sv::EventControl::AtEdge), ArrayRef(predicate), [&]() {
5239 if (op.getMessageAttr().getValue().empty())
5240 buildImmediateVerifOp(
5241 builder,
"assume", predicate,
5242 circt::sv::DeferAssertAttr::get(
5243 builder.getContext(), circt::sv::DeferAssert::Immediate),
5246 buildImmediateVerifOp(
5247 builder,
"assume", predicate,
5248 circt::sv::DeferAssertAttr::get(
5249 builder.getContext(), circt::sv::DeferAssert::Immediate),
5250 assumeLabel, op.getMessageAttr(), messageOps);
5255LogicalResult FIRRTLLowering::visitStmt(AttachOp op) {
5257 if (op.getAttached().size() < 2)
5260 SmallVector<Value, 4> inoutValues;
5261 for (
auto v : op.getAttached()) {
5262 inoutValues.push_back(getPossiblyInoutLoweredValue(v));
5263 if (!inoutValues.back()) {
5267 inoutValues.pop_back();
5271 if (!isa<hw::InOutType>(inoutValues.back().getType()))
5272 return op.emitError(
"operand isn't an inout type");
5275 if (inoutValues.size() < 2)
5286 bool isAttachInternalOnly =
5287 llvm::none_of(inoutValues, [](
auto v) {
return isa<BlockArgument>(v); });
5289 if (isAttachInternalOnly) {
5290 auto v0 = inoutValues.front();
5291 for (
auto v : inoutValues) {
5294 v.replaceAllUsesWith(v0);
5301 circuitState.addMacroDecl(builder.getStringAttr(
"SYNTHESIS"));
5302 circuitState.addMacroDecl(builder.getStringAttr(
"VERILATOR"));
5307 SmallVector<Value, 4> values;
5308 for (
auto inoutValue : inoutValues)
5309 values.push_back(getReadValue(inoutValue));
5311 for (
size_t i1 = 0, e = inoutValues.size(); i1 != e; ++i1) {
5312 for (
size_t i2 = 0; i2 != e; ++i2)
5314 builder.create<
sv::AssignOp>(inoutValues[i1], values[i2]);
5323 builder.create<sv::VerbatimOp>(
5324 "`error \"Verilator does not support alias and thus "
5326 "arbitrarily connect bidirectional wires and ports\"");
5328 [&]() { builder.create<sv::AliasOp>(inoutValues); });
5334LogicalResult FIRRTLLowering::visitStmt(BindOp op) {
5335 builder.create<sv::BindOp>(op.getInstanceAttr());
5339LogicalResult FIRRTLLowering::fixupLTLOps() {
5340 if (ltlOpFixupWorklist.empty())
5342 LLVM_DEBUG(llvm::dbgs() <<
"Fixing up " << ltlOpFixupWorklist.size()
5346 for (
unsigned i = 0, e = ltlOpFixupWorklist.size(); i != e; ++i)
5347 for (
auto *user : ltlOpFixupWorklist[i]->getUsers())
5348 if (isa<
hw::WireOp>(user))
5349 ltlOpFixupWorklist.insert(user);
5352 while (!ltlOpFixupWorklist.empty()) {
5353 auto *op = ltlOpFixupWorklist.pop_back_val();
5356 if (
auto opIntf = dyn_cast_or_null<mlir::InferTypeOpInterface>(op)) {
5357 LLVM_DEBUG(llvm::dbgs() <<
"- Update " << *op <<
"\n");
5358 SmallVector<Type, 2> types;
5359 auto result = opIntf.inferReturnTypes(
5360 op->getContext(), op->getLoc(), op->getOperands(),
5361 op->getAttrDictionary(), op->getPropertiesStorage(), op->getRegions(),
5365 assert(types.size() == op->getNumResults());
5369 for (
auto [result, type] :
llvm::zip(op->getResults(), types)) {
5370 if (result.getType() == type)
5372 LLVM_DEBUG(llvm::dbgs()
5373 <<
" - Result #" << result.getResultNumber() <<
" from "
5374 << result.getType() <<
" to " << type <<
"\n");
5375 result.setType(type);
5376 for (
auto *user : result.getUsers())
5378 ltlOpFixupWorklist.insert(user);
5383 if (
auto wireOp = dyn_cast<hw::WireOp>(op)) {
5384 if (isa<ltl::SequenceType, ltl::PropertyType>(wireOp.getType())) {
5385 wireOp.replaceAllUsesWith(wireOp.getInput());
5386 LLVM_DEBUG(llvm::dbgs() <<
"- Remove " << wireOp <<
"\n");
5387 if (wireOp.use_empty())
5394 SmallPtrSet<Operation *, 4> usersReported;
5395 for (
auto *user : op->getUsers()) {
5396 if (!usersReported.insert(user).second)
5398 if (isa<ltl::LTLDialect, verif::VerifDialect>(user->getDialect()))
5400 if (isa<hw::WireOp>(user))
5402 auto d = op->emitError(
5403 "verification operation used in a non-verification context");
5404 d.attachNote(user->getLoc())
5405 <<
"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.
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.
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