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);
1693 if (
auto castOp = dyn_cast<mlir::UnrealizedConversionCastOp>(op))
1694 return visitUnrealizedConversionCast(castOp);
1699 LogicalResult visitDecl(WireOp op);
1700 LogicalResult visitDecl(NodeOp op);
1701 LogicalResult visitDecl(RegOp op);
1702 LogicalResult visitDecl(RegResetOp op);
1703 LogicalResult visitDecl(MemOp op);
1704 LogicalResult visitDecl(InstanceOp oldInstance);
1705 LogicalResult visitDecl(VerbatimWireOp op);
1706 LogicalResult visitDecl(ContractOp op);
1709 LogicalResult lowerNoopCast(Operation *op);
1710 LogicalResult visitExpr(AsSIntPrimOp op);
1711 LogicalResult visitExpr(AsUIntPrimOp op);
1712 LogicalResult visitExpr(AsClockPrimOp op);
1713 LogicalResult visitExpr(AsAsyncResetPrimOp op) {
return lowerNoopCast(op); }
1715 LogicalResult visitExpr(HWStructCastOp op);
1716 LogicalResult visitExpr(BitCastOp op);
1718 visitUnrealizedConversionCast(mlir::UnrealizedConversionCastOp op);
1719 LogicalResult visitExpr(CvtPrimOp op);
1720 LogicalResult visitExpr(NotPrimOp op);
1721 LogicalResult visitExpr(NegPrimOp op);
1722 LogicalResult visitExpr(PadPrimOp op);
1723 LogicalResult visitExpr(XorRPrimOp op);
1724 LogicalResult visitExpr(AndRPrimOp op);
1725 LogicalResult visitExpr(OrRPrimOp op);
1728 template <
typename ResultUnsignedOpType,
1729 typename ResultSignedOpType = ResultUnsignedOpType>
1730 LogicalResult lowerBinOp(Operation *op);
1731 template <
typename ResultOpType>
1732 LogicalResult lowerBinOpToVariadic(Operation *op);
1734 template <
typename ResultOpType>
1735 LogicalResult lowerElementwiseLogicalOp(Operation *op);
1737 LogicalResult lowerCmpOp(Operation *op, ICmpPredicate signedOp,
1738 ICmpPredicate unsignedOp);
1739 template <
typename SignedOp,
typename Un
signedOp>
1740 LogicalResult lowerDivLikeOp(Operation *op);
1742 LogicalResult visitExpr(CatPrimOp op);
1744 LogicalResult visitExpr(AndPrimOp op) {
1745 return lowerBinOpToVariadic<comb::AndOp>(op);
1747 LogicalResult visitExpr(OrPrimOp op) {
1748 return lowerBinOpToVariadic<comb::OrOp>(op);
1750 LogicalResult visitExpr(XorPrimOp op) {
1751 return lowerBinOpToVariadic<comb::XorOp>(op);
1753 LogicalResult visitExpr(ElementwiseOrPrimOp op) {
1754 return lowerElementwiseLogicalOp<comb::OrOp>(op);
1756 LogicalResult visitExpr(ElementwiseAndPrimOp op) {
1757 return lowerElementwiseLogicalOp<comb::AndOp>(op);
1759 LogicalResult visitExpr(ElementwiseXorPrimOp op) {
1760 return lowerElementwiseLogicalOp<comb::XorOp>(op);
1762 LogicalResult visitExpr(AddPrimOp op) {
1763 return lowerBinOpToVariadic<comb::AddOp>(op);
1765 LogicalResult visitExpr(EQPrimOp op) {
1766 return lowerCmpOp(op, ICmpPredicate::eq, ICmpPredicate::eq);
1768 LogicalResult visitExpr(NEQPrimOp op) {
1769 return lowerCmpOp(op, ICmpPredicate::ne, ICmpPredicate::ne);
1771 LogicalResult visitExpr(LTPrimOp op) {
1772 return lowerCmpOp(op, ICmpPredicate::slt, ICmpPredicate::ult);
1774 LogicalResult visitExpr(LEQPrimOp op) {
1775 return lowerCmpOp(op, ICmpPredicate::sle, ICmpPredicate::ule);
1777 LogicalResult visitExpr(GTPrimOp op) {
1778 return lowerCmpOp(op, ICmpPredicate::sgt, ICmpPredicate::ugt);
1780 LogicalResult visitExpr(GEQPrimOp op) {
1781 return lowerCmpOp(op, ICmpPredicate::sge, ICmpPredicate::uge);
1784 LogicalResult visitExpr(SubPrimOp op) {
return lowerBinOp<comb::SubOp>(op); }
1785 LogicalResult visitExpr(MulPrimOp op) {
1786 return lowerBinOpToVariadic<comb::MulOp>(op);
1788 LogicalResult visitExpr(DivPrimOp op) {
1789 return lowerDivLikeOp<comb::DivSOp, comb::DivUOp>(op);
1791 LogicalResult visitExpr(RemPrimOp op) {
1792 return lowerDivLikeOp<comb::ModSOp, comb::ModUOp>(op);
1796 LogicalResult visitExpr(IsXIntrinsicOp op);
1797 LogicalResult visitExpr(PlusArgsTestIntrinsicOp op);
1798 LogicalResult visitExpr(PlusArgsValueIntrinsicOp op);
1799 LogicalResult visitStmt(FPGAProbeIntrinsicOp op);
1800 LogicalResult visitExpr(ClockInverterIntrinsicOp op);
1801 LogicalResult visitExpr(ClockDividerIntrinsicOp op);
1802 LogicalResult visitExpr(SizeOfIntrinsicOp op);
1803 LogicalResult visitExpr(ClockGateIntrinsicOp op);
1804 LogicalResult visitExpr(LTLAndIntrinsicOp op);
1805 LogicalResult visitExpr(LTLOrIntrinsicOp op);
1806 LogicalResult visitExpr(LTLIntersectIntrinsicOp op);
1807 LogicalResult visitExpr(LTLDelayIntrinsicOp op);
1808 LogicalResult visitExpr(LTLConcatIntrinsicOp op);
1809 LogicalResult visitExpr(LTLRepeatIntrinsicOp op);
1810 LogicalResult visitExpr(LTLGoToRepeatIntrinsicOp op);
1811 LogicalResult visitExpr(LTLNonConsecutiveRepeatIntrinsicOp op);
1812 LogicalResult visitExpr(LTLNotIntrinsicOp op);
1813 LogicalResult visitExpr(LTLImplicationIntrinsicOp op);
1814 LogicalResult visitExpr(LTLUntilIntrinsicOp op);
1815 LogicalResult visitExpr(LTLEventuallyIntrinsicOp op);
1816 LogicalResult visitExpr(LTLClockIntrinsicOp op);
1818 template <
typename TargetOp,
typename IntrinsicOp>
1819 LogicalResult lowerVerifIntrinsicOp(IntrinsicOp op);
1820 LogicalResult visitStmt(VerifAssertIntrinsicOp op);
1821 LogicalResult visitStmt(VerifAssumeIntrinsicOp op);
1822 LogicalResult visitStmt(VerifCoverIntrinsicOp op);
1823 LogicalResult visitStmt(VerifRequireIntrinsicOp op);
1824 LogicalResult visitStmt(VerifEnsureIntrinsicOp op);
1825 LogicalResult visitExpr(HasBeenResetIntrinsicOp op);
1826 LogicalResult visitStmt(UnclockedAssumeIntrinsicOp op);
1829 LogicalResult visitExpr(BitsPrimOp op);
1830 LogicalResult visitExpr(InvalidValueOp op);
1831 LogicalResult visitExpr(HeadPrimOp op);
1832 LogicalResult visitExpr(ShlPrimOp op);
1833 LogicalResult visitExpr(ShrPrimOp op);
1834 LogicalResult visitExpr(DShlPrimOp op) {
1835 return lowerDivLikeOp<comb::ShlOp, comb::ShlOp>(op);
1837 LogicalResult visitExpr(DShrPrimOp op) {
1838 return lowerDivLikeOp<comb::ShrSOp, comb::ShrUOp>(op);
1840 LogicalResult visitExpr(DShlwPrimOp op) {
1841 return lowerDivLikeOp<comb::ShlOp, comb::ShlOp>(op);
1843 LogicalResult visitExpr(TailPrimOp op);
1844 LogicalResult visitExpr(MuxPrimOp op);
1845 LogicalResult visitExpr(Mux2CellIntrinsicOp op);
1846 LogicalResult visitExpr(Mux4CellIntrinsicOp op);
1847 LogicalResult visitExpr(MultibitMuxOp op);
1848 LogicalResult visitExpr(VerbatimExprOp op);
1849 LogicalResult visitExpr(XMRRefOp op);
1850 LogicalResult visitExpr(XMRDerefOp op);
1853 LogicalResult visitExpr(TimeOp op);
1854 LogicalResult visitExpr(HierarchicalModuleNameOp op);
1857 LogicalResult lowerVerificationStatement(
1858 Operation *op, StringRef labelPrefix, Value clock, Value predicate,
1859 Value enable, StringAttr messageAttr, ValueRange operands,
1860 StringAttr nameAttr,
bool isConcurrent, EventControl eventControl);
1862 LogicalResult visitStmt(SkipOp op);
1864 FailureOr<bool> lowerConnect(Value dest, Value srcVal);
1865 LogicalResult visitStmt(ConnectOp op);
1866 LogicalResult visitStmt(MatchingConnectOp op);
1867 LogicalResult visitStmt(ForceOp op);
1869 std::optional<Value> getLoweredFmtOperand(Value operand);
1870 LogicalResult loweredFmtOperands(ValueRange operands,
1871 SmallVectorImpl<Value> &loweredOperands);
1872 FailureOr<Value> callFileDescriptorLib(
const FileDescriptorInfo &info);
1876 LogicalResult lowerStatementWithFd(
1877 const FileDescriptorInfo &fileDescriptorInfo, Value clock, Value cond,
1878 const std::function<LogicalResult(Value)> &fn,
bool usePrintfCond);
1882 LogicalResult visitPrintfLike(T op,
1883 const FileDescriptorInfo &fileDescriptorInfo,
1884 bool usePrintfCond);
1885 LogicalResult visitStmt(PrintFOp op) {
return visitPrintfLike(op, {},
true); }
1886 LogicalResult visitStmt(FPrintFOp op);
1887 LogicalResult visitStmt(FFlushOp op);
1888 LogicalResult visitStmt(StopOp op);
1889 LogicalResult visitStmt(AssertOp op);
1890 LogicalResult visitStmt(AssumeOp op);
1891 LogicalResult visitStmt(CoverOp op);
1892 LogicalResult visitStmt(AttachOp op);
1893 LogicalResult visitStmt(RefForceOp op);
1894 LogicalResult visitStmt(RefForceInitialOp op);
1895 LogicalResult visitStmt(RefReleaseOp op);
1896 LogicalResult visitStmt(RefReleaseInitialOp op);
1897 LogicalResult visitStmt(BindOp op);
1899 FailureOr<Value> lowerSubindex(SubindexOp op, Value input);
1900 FailureOr<Value> lowerSubaccess(SubaccessOp op, Value input);
1901 FailureOr<Value> lowerSubfield(SubfieldOp op, Value input);
1903 LogicalResult fixupLTLOps();
1906 return circuitState.lowerType(type, builder.getLoc());
1914 CircuitLoweringState &circuitState;
1917 ImplicitLocOpBuilder builder;
1922 DenseMap<Value, Value> valueMapping;
1926 DenseMap<Value, Value> fromClockMapping;
1930 DenseMap<Attribute, Value> hwConstantMap;
1931 DenseMap<std::pair<Attribute, Type>, Attribute> hwAggregateConstantMap;
1935 DenseMap<unsigned, Value> hwConstantXMap;
1936 DenseMap<Type, Value> hwConstantZMap;
1942 DenseMap<Value, Value> readInOutCreated;
1945 DenseMap<StringAttr, sv::RegOp> fileNameToFileDescriptor;
1949 using AlwaysKeyType = std::tuple<
Block *, sv::EventControl, Value,
1950 sv::ResetType, sv::EventControl, Value>;
1967 llvm::MapVector<Value, Value> backedges;
1974 DenseSet<Operation *> maybeUnusedValues;
1976 void maybeUnused(Operation *op) { maybeUnusedValues.insert(op); }
1977 void maybeUnused(Value value) {
1978 if (
auto *op = value.getDefiningOp())
1990 SetVector<Operation *> ltlOpFixupWorklist;
1995 SmallVector<std::pair<Block::iterator, Block::iterator>> worklist;
1997 void addToWorklist(Block &block) {
1998 worklist.push_back({block.begin(), block.end()});
2000 void addToWorklist(Region ®ion) {
2001 for (
auto &block :
llvm::reverse(region))
2002 addToWorklist(block);
2013LogicalResult FIRRTLModuleLowering::lowerFileBody(emit::FileOp fileOp) {
2014 OpBuilder b(&getContext());
2015 fileOp->walk([&](Operation *op) {
2016 if (
auto bindOp = dyn_cast<BindOp>(op)) {
2017 b.setInsertionPointAfter(bindOp);
2018 b.create<sv::BindOp>(bindOp.getLoc(), bindOp.getInstanceAttr());
2026FIRRTLModuleLowering::lowerBody(Operation *op,
2028 if (
auto moduleOp = dyn_cast<hw::HWModuleOp>(op))
2030 if (
auto formalOp = dyn_cast<verif::FormalOp>(op))
2032 if (
auto simulationOp = dyn_cast<verif::SimulationOp>(op))
2034 if (
auto fileOp = dyn_cast<emit::FileOp>(op))
2035 return lowerFileBody(fileOp);
2040LogicalResult FIRRTLLowering::run() {
2043 for (
auto arg : theModule.
getBodyBlock()->getArguments())
2044 if (failed(setLowering(arg, arg)))
2051 addToWorklist(theModule.getBody());
2052 SmallVector<Operation *, 16> opsToRemove;
2054 while (!worklist.empty()) {
2055 auto &[opsIt, opsEnd] = worklist.back();
2056 if (opsIt == opsEnd) {
2057 worklist.pop_back();
2060 Operation *op = &*opsIt++;
2062 builder.setInsertionPoint(op);
2063 builder.setLoc(op->getLoc());
2064 auto done = succeeded(dispatchVisitor(op));
2065 circuitState.processRemainingAnnotations(op,
AnnotationSet(op));
2067 opsToRemove.push_back(op);
2069 switch (handleUnloweredOp(op)) {
2070 case AlreadyLowered:
2073 opsToRemove.push_back(op);
2075 case LoweringFailure:
2087 for (
auto &[backedge, value] : backedges) {
2088 SmallVector<Location> driverLocs;
2094 if (backedge == value) {
2095 Location edgeLoc = backedge.getLoc();
2096 if (driverLocs.empty()) {
2097 mlir::emitError(edgeLoc,
"sink does not have a driver");
2099 auto diag = mlir::emitError(edgeLoc,
"sink in combinational loop");
2100 for (
auto loc : driverLocs)
2101 diag.attachNote(loc) <<
"through driver here";
2107 auto *it = backedges.find(value);
2108 if (it == backedges.end())
2111 driverLocs.push_back(value.getLoc());
2114 if (
auto *defOp = backedge.getDefiningOp())
2115 maybeUnusedValues.erase(defOp);
2116 backedge.replaceAllUsesWith(value);
2124 while (!opsToRemove.empty()) {
2125 auto *op = opsToRemove.pop_back_val();
2130 for (
auto result : op->getResults()) {
2134 auto builder = OpBuilder::atBlockBegin(theModule.getBodyBlock());
2136 builder.getIntegerType(0), 0);
2137 maybeUnusedValues.insert(zeroI0);
2139 result.replaceAllUsesWith(zeroI0);
2142 if (!op->use_empty()) {
2143 auto d = op->emitOpError(
2144 "still has uses; should remove ops in reverse order of visitation");
2145 SmallPtrSet<Operation *, 2> visited;
2146 for (
auto *user : op->getUsers())
2147 if (visited.insert(user).second)
2148 d.attachNote(user->
getLoc())
2149 <<
"used by " << user->
getName() <<
" op";
2152 maybeUnusedValues.erase(op);
2157 while (!maybeUnusedValues.empty()) {
2158 auto it = maybeUnusedValues.begin();
2160 maybeUnusedValues.erase(it);
2161 if (!isOpTriviallyDead(op))
2163 for (
auto operand : op->getOperands())
2164 if (auto *defOp = operand.getDefiningOp())
2165 maybeUnusedValues.insert(defOp);
2171 if (failed(fixupLTLOps()))
2182Value FIRRTLLowering::getOrCreateClockConstant(seq::ClockConst clock) {
2183 auto attr = seq::ClockConstAttr::get(theModule.getContext(), clock);
2185 auto &entry = hwConstantMap[attr];
2189 OpBuilder entryBuilder(&theModule.getBodyBlock()->front());
2190 entry = entryBuilder.create<seq::ConstClockOp>(builder.getLoc(), attr);
2196Value FIRRTLLowering::getOrCreateIntConstant(
const APInt &value) {
2197 auto attr = builder.getIntegerAttr(
2198 builder.getIntegerType(value.getBitWidth()), value);
2200 auto &entry = hwConstantMap[attr];
2204 OpBuilder entryBuilder(&theModule.getBodyBlock()->front());
2205 entry = entryBuilder.create<
hw::ConstantOp>(builder.getLoc(), attr);
2211Attribute FIRRTLLowering::getOrCreateAggregateConstantAttribute(Attribute value,
2214 if (hw::type_isa<IntegerType>(type))
2215 return builder.getIntegerAttr(type, cast<IntegerAttr>(value).getValue());
2217 auto cache = hwAggregateConstantMap.lookup({value, type});
2222 SmallVector<Attribute> values;
2223 for (
auto e :
llvm::enumerate(cast<ArrayAttr>(value))) {
2225 if (
auto array = hw::type_dyn_cast<hw::ArrayType>(type))
2226 subType = array.getElementType();
2227 else if (
auto structType = hw::type_dyn_cast<hw::StructType>(type))
2228 subType = structType.getElements()[e.index()].type;
2230 assert(
false &&
"type must be either array or struct");
2232 values.push_back(getOrCreateAggregateConstantAttribute(e.value(), subType));
2236 if (hw::type_isa<hw::ArrayType>(type))
2237 std::reverse(values.begin(), values.end());
2239 auto &entry = hwAggregateConstantMap[{value, type}];
2240 entry = builder.getArrayAttr(values);
2248 const std::function<LogicalResult()> &fn) {
2249 assert(failedOperand &&
"Should be called on the failed operand");
2257Value FIRRTLLowering::getOrCreateXConstant(
unsigned numBits) {
2259 auto &entry = hwConstantXMap[numBits];
2263 OpBuilder entryBuilder(&theModule.getBodyBlock()->front());
2264 entry = entryBuilder.create<sv::ConstantXOp>(
2265 builder.getLoc(), entryBuilder.getIntegerType(numBits));
2269Value FIRRTLLowering::getOrCreateZConstant(Type type) {
2270 auto &entry = hwConstantZMap[type];
2272 OpBuilder entryBuilder(&theModule.getBodyBlock()->front());
2273 entry = entryBuilder.create<sv::ConstantZOp>(builder.getLoc(), type);
2282Value FIRRTLLowering::getPossiblyInoutLoweredValue(Value value) {
2284 if (
auto lowering = valueMapping.lookup(value)) {
2285 assert(!isa<FIRRTLType>(lowering.getType()) &&
2286 "Lowered value should be a non-FIRRTL value");
2295Value FIRRTLLowering::getLoweredValue(Value value) {
2296 auto result = getPossiblyInoutLoweredValue(value);
2302 if (isa<hw::InOutType>(result.getType()))
2303 return getReadValue(result);
2309Value FIRRTLLowering::getLoweredNonClockValue(Value value) {
2310 auto result = getLoweredValue(value);
2314 if (hw::type_isa<seq::ClockType>(result.getType()))
2315 return getNonClockValue(result);
2323Value FIRRTLLowering::getExtOrTruncAggregateValue(Value array,
2326 bool allowTruncate) {
2327 SmallVector<Value> resultBuffer;
2332 auto srcWidth = firrtl::type_cast<IntType>(sourceType).getWidthOrSentinel();
2333 auto destWidth = firrtl::type_cast<IntType>(destType).getWidthOrSentinel();
2334 auto resultType = builder.getIntegerType(destWidth);
2336 if (srcWidth == destWidth)
2339 if (srcWidth > destWidth) {
2343 builder.emitError(
"operand should not be a truncation");
2347 if (firrtl::type_cast<IntType>(sourceType).isSigned())
2348 return comb::createOrFoldSExt(value, resultType, builder);
2349 auto zero = getOrCreateIntConstant(destWidth - srcWidth, 0);
2357 return TypeSwitch<FIRRTLBaseType, LogicalResult>(srcType)
2358 .Case<FVectorType>([&](
auto srcVectorType) {
2359 auto destVectorType = firrtl::type_cast<FVectorType>(destType);
2360 unsigned size = resultBuffer.size();
2361 unsigned indexWidth =
2363 for (
size_t i = 0, e = std::min(srcVectorType.getNumElements(),
2364 destVectorType.getNumElements());
2366 auto iIdx = getOrCreateIntConstant(indexWidth, i);
2368 if (failed(recurse(arrayIndex, srcVectorType.getElementType(),
2369 destVectorType.getElementType())))
2372 SmallVector<Value> temp(resultBuffer.begin() + size,
2373 resultBuffer.end());
2375 resultBuffer.resize(size);
2376 resultBuffer.push_back(array);
2379 .Case<BundleType>([&](BundleType srcStructType) {
2380 auto destStructType = firrtl::type_cast<BundleType>(destType);
2381 unsigned size = resultBuffer.size();
2384 if (destStructType.getNumElements() != srcStructType.getNumElements())
2387 for (
auto elem :
llvm::enumerate(destStructType)) {
2388 auto structExtract =
2390 if (failed(recurse(structExtract,
2391 srcStructType.getElementType(elem.index()),
2392 destStructType.getElementType(elem.index()))))
2395 SmallVector<Value> temp(resultBuffer.begin() + size,
2396 resultBuffer.end());
2399 resultBuffer.resize(size);
2400 resultBuffer.push_back(newStruct);
2403 .Case<IntType>([&](
auto) {
2404 if (
auto result = cast(src, srcType, destType)) {
2405 resultBuffer.push_back(result);
2410 .Default([&](
auto) {
return failure(); });
2413 if (failed(recurse(array, sourceType, destType)))
2416 assert(resultBuffer.size() == 1 &&
2417 "resultBuffer must only contain a result array if `success` is true");
2418 return resultBuffer[0];
2426Value FIRRTLLowering::getLoweredAndExtendedValue(Value value, Type destType) {
2427 assert(type_isa<FIRRTLBaseType>(value.getType()) &&
2428 type_isa<FIRRTLBaseType>(destType) &&
2429 "input/output value should be FIRRTL");
2432 auto destWidth = type_cast<FIRRTLBaseType>(destType).getBitWidthOrSentinel();
2433 if (destWidth == -1)
2436 auto result = getLoweredValue(value);
2448 return getOrCreateIntConstant(destWidth, 0);
2452 cast<FIRRTLBaseType>(value.getType()).getBitWidthOrSentinel()) {
2454 auto loweredDstType =
lowerType(destType);
2455 if (result.getType() != loweredDstType &&
2456 (isa<hw::TypeAliasType>(result.getType()) ||
2457 isa<hw::TypeAliasType>(loweredDstType))) {
2458 return builder.createOrFold<
hw::BitcastOp>(loweredDstType, result);
2462 if (isa<hw::ArrayType, hw::StructType>(result.getType())) {
2464 if (destType == value.getType())
2467 return getExtOrTruncAggregateValue(
2468 result, type_cast<FIRRTLBaseType>(value.getType()),
2469 type_cast<FIRRTLBaseType>(destType),
2473 if (isa<seq::ClockType>(result.getType())) {
2475 if (destType == value.getType())
2477 builder.emitError(
"cannot use clock type as an integer");
2481 auto intResultType = dyn_cast<IntegerType>(result.getType());
2482 if (!intResultType) {
2483 builder.emitError(
"operand of type ")
2484 << result.getType() <<
" cannot be used as an integer";
2488 auto srcWidth = intResultType.getWidth();
2489 if (srcWidth ==
unsigned(destWidth))
2492 if (srcWidth >
unsigned(destWidth)) {
2493 builder.emitError(
"operand should not be a truncation");
2497 auto resultType = builder.getIntegerType(destWidth);
2501 type_cast<FIRRTLBaseType>(value.getType()).getPassiveType();
2502 if (type_cast<IntType>(valueFIRType).isSigned())
2503 return comb::createOrFoldSExt(result, resultType, builder);
2505 auto zero = getOrCreateIntConstant(destWidth - srcWidth, 0);
2514Value FIRRTLLowering::getLoweredAndExtOrTruncValue(Value value, Type destType) {
2515 assert(type_isa<FIRRTLBaseType>(value.getType()) &&
2516 type_isa<FIRRTLBaseType>(destType) &&
2517 "input/output value should be FIRRTL");
2520 auto destWidth = type_cast<FIRRTLBaseType>(destType).getBitWidthOrSentinel();
2521 if (destWidth == -1)
2524 auto result = getLoweredValue(value);
2536 return getOrCreateIntConstant(destWidth, 0);
2540 if (isa<hw::ArrayType, hw::StructType>(result.getType())) {
2542 if (destType == value.getType())
2545 return getExtOrTruncAggregateValue(
2546 result, type_cast<FIRRTLBaseType>(value.getType()),
2547 type_cast<FIRRTLBaseType>(destType),
2551 auto srcWidth = type_cast<IntegerType>(result.getType()).getWidth();
2552 if (srcWidth ==
unsigned(destWidth))
2558 if (srcWidth >
unsigned(destWidth)) {
2559 auto resultType = builder.getIntegerType(destWidth);
2563 auto resultType = builder.getIntegerType(destWidth);
2567 type_cast<FIRRTLBaseType>(value.getType()).getPassiveType();
2568 if (type_cast<IntType>(valueFIRType).isSigned())
2569 return comb::createOrFoldSExt(result, resultType, builder);
2571 auto zero = getOrCreateIntConstant(destWidth - srcWidth, 0);
2585std::optional<Value> FIRRTLLowering::getLoweredFmtOperand(Value operand) {
2587 if (type_isa<FStringType>(operand.getType())) {
2588 if (isa<TimeOp>(operand.getDefiningOp()))
2589 return builder.create<sv::TimeOp>();
2590 if (isa<HierarchicalModuleNameOp>(operand.getDefiningOp()))
2594 auto loweredValue = getLoweredValue(operand);
2595 if (!loweredValue) {
2599 loweredValue = getOrCreateIntConstant(1, 0);
2604 if (
auto intTy = firrtl::type_cast<IntType>(operand.getType()))
2605 if (intTy.isSigned())
2606 loweredValue = builder.create<sv::SystemFunctionOp>(
2607 loweredValue.getType(),
"signed", loweredValue);
2609 return loweredValue;
2613FIRRTLLowering::loweredFmtOperands(mlir::ValueRange operands,
2614 SmallVectorImpl<Value> &loweredOperands) {
2615 for (
auto operand : operands) {
2616 std::optional<Value> loweredValue = getLoweredFmtOperand(operand);
2621 loweredOperands.push_back(*loweredValue);
2626LogicalResult FIRRTLLowering::lowerStatementWithFd(
2627 const FileDescriptorInfo &fileDescriptor, Value clock, Value cond,
2628 const std::function<LogicalResult(Value)> &fn,
bool usePrintfCond) {
2630 bool failed =
false;
2631 circuitState.addMacroDecl(builder.getStringAttr(
"SYNTHESIS"));
2632 addToIfDefBlock(
"SYNTHESIS", std::function<
void()>(), [&]() {
2633 addToAlwaysBlock(clock, [&]() {
2636 circuitState.usedPrintf =
true;
2638 circuitState.addFragment(theModule,
"PRINTF_COND_FRAGMENT");
2641 Value ifCond = cond;
2642 if (usePrintfCond) {
2644 builder.create<sv::MacroRefExprOp>(cond.getType(),
"PRINTF_COND_");
2645 ifCond = builder.createOrFold<
comb::AndOp>(ifCond, cond,
true);
2648 addIfProceduralBlock(ifCond, [&]() {
2652 if (fileDescriptor.isDefaultFd()) {
2654 fd = builder.create<hw::ConstantOp>(APInt(32, 0x80000002));
2657 auto fdOrError = callFileDescriptorLib(fileDescriptor);
2658 if (llvm::failed(fdOrError)) {
2664 failed = llvm::failed(fn(fd));
2668 return failure(failed);
2672FIRRTLLowering::callFileDescriptorLib(
const FileDescriptorInfo &info) {
2673 circuitState.usedFileDescriptorLib =
true;
2674 circuitState.addFragment(theModule,
"CIRCT_LIB_LOGGING_FRAGMENT");
2677 if (
info.isSubstitutionRequired()) {
2678 SmallVector<Value> fileNameOperands;
2679 if (failed(loweredFmtOperands(
info.getSubstitutions(), fileNameOperands)))
2683 .create<sv::SFormatFOp>(
info.getOutputFileFormat(),
2688 fileName = builder.create<sv::ConstantStrOp>(
info.getOutputFileFormat())
2693 .create<sv::FuncCallProceduralOp>(
2694 mlir::TypeRange{builder.getIntegerType(32)},
2695 builder.getStringAttr(
"__circt_lib_logging::FileDescriptor::get"),
2696 ValueRange{fileName})
2706LogicalResult FIRRTLLowering::setLowering(Value orig, Value result) {
2707 if (
auto origType = dyn_cast<FIRRTLType>(orig.getType())) {
2708 assert((!result || !type_isa<FIRRTLType>(result.getType())) &&
2709 "Lowering didn't turn a FIRRTL value into a non-FIRRTL value");
2713 auto srcWidth = baseType.getPassiveType().getBitWidthOrSentinel();
2716 if (srcWidth != -1) {
2718 assert((srcWidth != 0) &&
2719 "Lowering produced value for zero width source");
2721 assert((srcWidth == 0) &&
2722 "Lowering produced null value but source wasn't zero width");
2726 assert(result &&
"Lowering of foreign type produced null value");
2729 auto &slot = valueMapping[orig];
2730 assert(!slot &&
"value lowered multiple times");
2737LogicalResult FIRRTLLowering::setPossiblyFoldedLowering(Value orig,
2741 if (
auto cst = dyn_cast_or_null<hw::ConstantOp>(result.getDefiningOp())) {
2742 auto &entry = hwConstantMap[cst.getValueAttr()];
2753 cst->moveBefore(&theModule.getBodyBlock()->front());
2757 return setLowering(orig, result);
2762template <
typename ResultOpType,
typename... CtorArgTypes>
2763LogicalResult FIRRTLLowering::setLoweringTo(Operation *orig,
2764 CtorArgTypes... args) {
2765 auto result = builder.createOrFold<ResultOpType>(args...);
2766 if (
auto *op = result.getDefiningOp())
2768 return setPossiblyFoldedLowering(orig->getResult(0), result);
2775template <
typename ResultOpType,
typename... CtorArgTypes>
2776LogicalResult FIRRTLLowering::setLoweringToLTL(Operation *orig,
2777 CtorArgTypes... args) {
2778 auto result = builder.createOrFold<ResultOpType>(args...);
2779 if (
auto *op = result.getDefiningOp())
2780 ltlOpFixupWorklist.insert(op);
2781 return setPossiblyFoldedLowering(orig->getResult(0), result);
2790Backedge FIRRTLLowering::createBackedge(Location loc, Type type) {
2791 auto backedge = backedgeBuilder.
get(type, loc);
2792 backedges.insert({backedge, backedge});
2800Backedge FIRRTLLowering::createBackedge(Value orig, Type type) {
2801 auto backedge = createBackedge(orig.getLoc(), type);
2802 (void)setLowering(orig, backedge);
2808bool FIRRTLLowering::updateIfBackedge(Value dest, Value src) {
2809 auto backedgeIt = backedges.find(dest);
2810 if (backedgeIt == backedges.end())
2812 backedgeIt->second = src;
2820void FIRRTLLowering::runWithInsertionPointAtEndOfBlock(
2821 const std::function<
void(
void)> &fn, Region ®ion) {
2825 auto oldIP = builder.saveInsertionPoint();
2827 builder.setInsertionPointToEnd(®ion.front());
2829 builder.restoreInsertionPoint(oldIP);
2833Value FIRRTLLowering::getReadValue(Value v) {
2834 Value result = readInOutCreated.lookup(v);
2840 auto oldIP = builder.saveInsertionPoint();
2841 if (
auto *vOp = v.getDefiningOp()) {
2842 builder.setInsertionPointAfter(vOp);
2846 builder.setInsertionPoint(&theModule.getBodyBlock()->front());
2851 if (
auto arrayIndexInout = v.getDefiningOp<sv::ArrayIndexInOutOp>()) {
2852 result = getReadValue(arrayIndexInout.getInput());
2854 arrayIndexInout.getIndex());
2859 builder.restoreInsertionPoint(oldIP);
2860 readInOutCreated.insert({v, result});
2864Value FIRRTLLowering::getNonClockValue(Value v) {
2865 auto it = fromClockMapping.try_emplace(v, Value{});
2867 ImplicitLocOpBuilder builder(v.getLoc(), v.getContext());
2868 builder.setInsertionPointAfterValue(v);
2869 it.first->second = builder.create<seq::FromClockOp>(v);
2871 return it.first->second;
2874void FIRRTLLowering::addToAlwaysBlock(
2875 sv::EventControl clockEdge, Value clock, sv::ResetType resetStyle,
2876 sv::EventControl resetEdge, Value reset,
2877 const std::function<
void(
void)> &body,
2878 const std::function<
void(
void)> &resetBody) {
2879 AlwaysKeyType key{builder.getBlock(), clockEdge, clock,
2880 resetStyle, resetEdge, reset};
2881 sv::AlwaysOp alwaysOp;
2882 sv::IfOp insideIfOp;
2883 std::tie(alwaysOp, insideIfOp) = alwaysBlocks.lookup(key);
2887 assert(resetStyle != sv::ResetType::NoReset);
2900 auto createIfOp = [&]() {
2903 insideIfOp = builder.create<sv::IfOp>(
2904 reset, []() {}, []() {});
2906 if (resetStyle == sv::ResetType::AsyncReset) {
2907 sv::EventControl events[] = {clockEdge, resetEdge};
2908 Value clocks[] = {clock, reset};
2910 alwaysOp = builder.create<sv::AlwaysOp>(events, clocks, [&]() {
2911 if (resetEdge == sv::EventControl::AtNegEdge)
2912 llvm_unreachable(
"negative edge for reset is not expected");
2916 alwaysOp = builder.create<sv::AlwaysOp>(clockEdge, clock, createIfOp);
2920 alwaysOp = builder.create<sv::AlwaysOp>(clockEdge, clock);
2921 insideIfOp =
nullptr;
2923 alwaysBlocks[key] = {alwaysOp, insideIfOp};
2927 assert(insideIfOp &&
"reset body must be initialized before");
2928 runWithInsertionPointAtEndOfBlock(resetBody, insideIfOp.getThenRegion());
2929 runWithInsertionPointAtEndOfBlock(body, insideIfOp.getElseRegion());
2931 runWithInsertionPointAtEndOfBlock(body, alwaysOp.getBody());
2937 alwaysOp->moveBefore(builder.getInsertionBlock(),
2938 builder.getInsertionPoint());
2941LogicalResult FIRRTLLowering::emitGuards(Location loc,
2942 ArrayRef<Attribute> guards,
2943 std::function<
void(
void)>
emit) {
2944 if (guards.empty()) {
2948 auto guard = dyn_cast<StringAttr>(guards[0]);
2950 return mlir::emitError(loc,
2951 "elements in `guards` array must be `StringAttr`");
2954 circuitState.addMacroDecl(builder.getStringAttr(guard.getValue()));
2955 LogicalResult result = LogicalResult::failure();
2956 addToIfDefBlock(guard.getValue(), [&]() {
2957 result = emitGuards(loc, guards.drop_front(), emit);
2962void FIRRTLLowering::addToIfDefBlock(StringRef cond,
2963 std::function<
void(
void)> thenCtor,
2964 std::function<
void(
void)> elseCtor) {
2965 auto condAttr = builder.getStringAttr(cond);
2966 auto op = ifdefBlocks.lookup({builder.getBlock(), condAttr});
2968 runWithInsertionPointAtEndOfBlock(thenCtor, op.getThenRegion());
2969 runWithInsertionPointAtEndOfBlock(elseCtor, op.getElseRegion());
2974 op->moveBefore(builder.getInsertionBlock(), builder.getInsertionPoint());
2976 ifdefBlocks[{builder.getBlock(), condAttr}] =
2977 builder.create<
sv::IfDefOp>(condAttr, thenCtor, elseCtor);
2981void FIRRTLLowering::addToInitialBlock(std::function<
void(
void)> body) {
2982 auto op = initialBlocks.lookup(builder.getBlock());
2984 runWithInsertionPointAtEndOfBlock(body, op.getBody());
2989 op->moveBefore(builder.getInsertionBlock(), builder.getInsertionPoint());
2991 initialBlocks[builder.getBlock()] = builder.create<sv::InitialOp>(body);
2995void FIRRTLLowering::addIfProceduralBlock(Value cond,
2996 std::function<
void(
void)> thenCtor,
2997 std::function<
void(
void)> elseCtor) {
3000 auto insertIt = builder.getInsertionPoint();
3001 if (insertIt != builder.getBlock()->begin())
3002 if (
auto ifOp = dyn_cast<sv::IfOp>(*--insertIt)) {
3003 if (ifOp.getCond() == cond) {
3004 runWithInsertionPointAtEndOfBlock(thenCtor, ifOp.getThenRegion());
3005 runWithInsertionPointAtEndOfBlock(elseCtor, ifOp.getElseRegion());
3010 builder.create<sv::IfOp>(cond, thenCtor, elseCtor);
3022FIRRTLLowering::UnloweredOpResult
3023FIRRTLLowering::handleUnloweredOp(Operation *op) {
3025 if (!op->getRegions().empty() && isa<FIRRTLDialect>(op->getDialect())) {
3026 op->emitOpError(
"must explicitly handle its regions");
3027 return LoweringFailure;
3034 if (!isa<FIRRTLDialect>(op->getDialect())) {
3036 for (
auto ®ion : op->getRegions())
3037 addToWorklist(region);
3038 for (
auto &operand : op->getOpOperands())
3039 if (auto lowered = getPossiblyInoutLoweredValue(operand.
get()))
3040 operand.set(lowered);
3041 for (
auto result : op->getResults())
3042 (void)setLowering(result, result);
3043 return AlreadyLowered;
3055 if (op->getNumResults() == 1) {
3056 auto resultType = op->getResult(0).getType();
3057 if (type_isa<FIRRTLBaseType>(resultType) &&
3059 (
isExpression(op) || isa<mlir::UnrealizedConversionCastOp>(op))) {
3061 (void)setLowering(op->getResult(0), Value());
3065 op->emitOpError(
"LowerToHW couldn't handle this operation");
3066 return LoweringFailure;
3069LogicalResult FIRRTLLowering::visitExpr(ConstantOp op) {
3072 return setLowering(op, Value());
3074 return setLowering(op, getOrCreateIntConstant(op.getValue()));
3077LogicalResult FIRRTLLowering::visitExpr(SpecialConstantOp op) {
3079 if (isa<ClockType>(op.getType())) {
3080 cst = getOrCreateClockConstant(op.getValue() ? seq::ClockConst::High
3081 :
seq::ClockConst::Low);
3083 cst = getOrCreateIntConstant(APInt( 1, op.getValue()));
3085 return setLowering(op, cst);
3088FailureOr<Value> FIRRTLLowering::lowerSubindex(SubindexOp op, Value input) {
3089 auto iIdx = getOrCreateIntConstant(
3091 firrtl::type_cast<FVectorType>(op.getInput().getType())
3098 if (isa<sv::InOutType>(input.getType()))
3099 result = builder.createOrFold<sv::ArrayIndexInOutOp>(input, iIdx);
3102 if (
auto *definingOp = result.getDefiningOp())
3107FailureOr<Value> FIRRTLLowering::lowerSubaccess(SubaccessOp op, Value input) {
3108 Value valueIdx = getLoweredAndExtOrTruncValue(
3110 UIntType::get(op->getContext(),
3112 firrtl::type_cast<FVectorType>(op.getInput().getType())
3113 .getNumElements())));
3115 op->emitError() <<
"input lowering failed";
3122 if (isa<sv::InOutType>(input.getType()))
3123 result = builder.createOrFold<sv::ArrayIndexInOutOp>(input, valueIdx);
3125 result = createArrayIndexing(input, valueIdx);
3126 if (
auto *definingOp = result.getDefiningOp())
3131FailureOr<Value> FIRRTLLowering::lowerSubfield(SubfieldOp op, Value input) {
3132 auto resultType =
lowerType(op->getResult(0).getType());
3133 if (!resultType || !input) {
3134 op->emitError() <<
"subfield type lowering failed";
3140 auto field = firrtl::type_cast<BundleType>(op.getInput().getType())
3141 .getElementName(op.getFieldIndex());
3143 if (isa<sv::InOutType>(input.getType()))
3144 result = builder.createOrFold<sv::StructFieldInOutOp>(input, field);
3147 if (
auto *definingOp = result.getDefiningOp())
3152LogicalResult FIRRTLLowering::visitExpr(SubindexOp op) {
3154 return setLowering(op, Value());
3156 auto input = getPossiblyInoutLoweredValue(op.getInput());
3158 return op.emitError() <<
"input lowering failed";
3160 auto result = lowerSubindex(op, input);
3163 return setLowering(op, *result);
3166LogicalResult FIRRTLLowering::visitExpr(SubaccessOp op) {
3168 return setLowering(op, Value());
3170 auto input = getPossiblyInoutLoweredValue(op.getInput());
3172 return op.emitError() <<
"input lowering failed";
3174 auto result = lowerSubaccess(op, input);
3177 return setLowering(op, *result);
3180LogicalResult FIRRTLLowering::visitExpr(SubfieldOp op) {
3183 if (getLoweredValue(op) || !op.getInput())
3187 return setLowering(op, Value());
3189 auto input = getPossiblyInoutLoweredValue(op.getInput());
3191 return op.emitError() <<
"input lowering failed";
3193 auto result = lowerSubfield(op, input);
3196 return setLowering(op, *result);
3199LogicalResult FIRRTLLowering::visitExpr(VectorCreateOp op) {
3200 auto resultType =
lowerType(op.getResult().getType());
3201 SmallVector<Value> operands;
3203 for (
auto oper :
llvm::reverse(op.getOperands())) {
3204 auto val = getLoweredValue(oper);
3207 operands.push_back(val);
3209 return setLoweringTo<hw::ArrayCreateOp>(op, resultType, operands);
3212LogicalResult FIRRTLLowering::visitExpr(BundleCreateOp op) {
3213 auto resultType =
lowerType(op.getResult().getType());
3214 SmallVector<Value> operands;
3215 for (
auto oper : op.getOperands()) {
3216 auto val = getLoweredValue(oper);
3219 operands.push_back(val);
3221 return setLoweringTo<hw::StructCreateOp>(op, resultType, operands);
3224LogicalResult FIRRTLLowering::visitExpr(FEnumCreateOp op) {
3227 return setLowering(op, Value());
3229 auto input = getLoweredValue(op.getInput());
3230 auto tagName = op.getFieldNameAttr();
3233 if (
auto structType = dyn_cast<hw::StructType>(type)) {
3234 auto enumType = structType.getFieldType(
"tag");
3235 auto enumAttr = hw::EnumFieldAttr::get(op.getLoc(), tagName, enumType);
3236 auto enumOp = builder.create<hw::EnumConstantOp>(enumAttr);
3237 auto unionType = structType.getFieldType(
"body");
3238 auto unionOp = builder.create<hw::UnionCreateOp>(unionType, tagName, input);
3239 SmallVector<Value> operands = {enumOp.getResult(), unionOp.getResult()};
3240 return setLoweringTo<hw::StructCreateOp>(op, structType, operands);
3243 return setLoweringTo<hw::EnumConstantOp>(
3244 op, hw::EnumFieldAttr::get(op.getLoc(), tagName, type));
3247LogicalResult FIRRTLLowering::visitExpr(AggregateConstantOp op) {
3248 auto resultType =
lowerType(op.getResult().getType());
3250 getOrCreateAggregateConstantAttribute(op.getFieldsAttr(), resultType);
3252 return setLoweringTo<hw::AggregateConstantOp>(op, resultType,
3253 cast<ArrayAttr>(attr));
3256LogicalResult FIRRTLLowering::visitExpr(IsTagOp op) {
3257 auto tagName = op.getFieldNameAttr();
3258 auto lhs = getLoweredValue(op.getInput());
3259 if (isa<hw::StructType>(lhs.getType()))
3261 auto enumField = hw::EnumFieldAttr::get(op.getLoc(), tagName, lhs.getType());
3262 auto rhs = builder.create<hw::EnumConstantOp>(enumField);
3263 return setLoweringTo<hw::EnumCmpOp>(op, lhs, rhs);
3266LogicalResult FIRRTLLowering::visitExpr(SubtagOp op) {
3269 return setLowering(op, Value());
3271 auto tagName = op.getFieldNameAttr();
3272 auto input = getLoweredValue(op.getInput());
3274 return setLoweringTo<hw::UnionExtractOp>(op, field, tagName);
3281LogicalResult FIRRTLLowering::visitDecl(WireOp op) {
3282 auto origResultType = op.getResult().getType();
3286 if (!type_isa<FIRRTLType>(origResultType)) {
3287 createBackedge(op.getResult(), origResultType);
3291 auto resultType =
lowerType(origResultType);
3295 if (resultType.isInteger(0)) {
3296 if (op.getInnerSym())
3297 return op.emitError(
"zero width wire is referenced by name [")
3298 << *op.getInnerSym() <<
"] (e.g. in an XMR) but must be removed";
3299 return setLowering(op.getResult(), Value());
3303 auto innerSym = lowerInnerSymbol(op);
3304 auto name = op.getNameAttr();
3307 auto wire = builder.create<hw::WireOp>(
3308 op.getLoc(), getOrCreateZConstant(resultType), name, innerSym);
3310 if (
auto svAttrs = sv::getSVAttributes(op))
3311 sv::setSVAttributes(wire, svAttrs);
3313 return setLowering(op.getResult(), wire);
3316LogicalResult FIRRTLLowering::visitDecl(VerbatimWireOp op) {
3317 auto resultTy =
lowerType(op.getType());
3320 resultTy = sv::InOutType::get(op.getContext(), resultTy);
3322 SmallVector<Value, 4> operands;
3323 operands.reserve(op.getSubstitutions().size());
3324 for (
auto operand : op.getSubstitutions()) {
3325 auto lowered = getLoweredValue(operand);
3328 operands.push_back(lowered);
3331 ArrayAttr symbols = op.getSymbolsAttr();
3333 symbols = ArrayAttr::get(op.getContext(), {});
3335 return setLoweringTo<sv::VerbatimExprSEOp>(op, resultTy, op.getTextAttr(),
3339LogicalResult FIRRTLLowering::visitDecl(NodeOp op) {
3340 auto operand = getLoweredValue(op.getInput());
3342 return handleZeroBit(op.getInput(), [&]() -> LogicalResult {
3343 if (op.getInnerSym())
3344 return op.emitError(
"zero width node is referenced by name [")
3345 << *op.getInnerSym()
3346 <<
"] (e.g. in an XMR) but must be "
3348 return setLowering(op.getResult(), Value());
3354 auto name = op.getNameAttr();
3355 auto innerSym = lowerInnerSymbol(op);
3358 operand = builder.create<hw::WireOp>(operand, name, innerSym);
3361 if (
auto svAttrs = sv::getSVAttributes(op)) {
3363 operand = builder.create<hw::WireOp>(operand, name);
3364 sv::setSVAttributes(operand.getDefiningOp(), svAttrs);
3367 return setLowering(op.getResult(), operand);
3370LogicalResult FIRRTLLowering::visitDecl(RegOp op) {
3371 auto resultType =
lowerType(op.getResult().getType());
3374 if (resultType.isInteger(0))
3375 return setLowering(op.getResult(), Value());
3377 Value clockVal = getLoweredValue(op.getClockVal());
3382 auto innerSym = lowerInnerSymbol(op);
3383 Backedge inputEdge = backedgeBuilder.
get(resultType);
3384 auto reg = builder.create<seq::FirRegOp>(inputEdge, clockVal,
3385 op.getNameAttr(), innerSym);
3388 if (
auto randomRegister = op->getAttr(
"firrtl.random_init_register"))
3389 reg->setAttr(
"firrtl.random_init_register", randomRegister);
3390 if (
auto randomStart = op->getAttr(
"firrtl.random_init_start"))
3391 reg->setAttr(
"firrtl.random_init_start", randomStart);
3392 if (
auto randomEnd = op->getAttr(
"firrtl.random_init_end"))
3393 reg->setAttr(
"firrtl.random_init_end", randomEnd);
3396 if (
auto svAttrs = sv::getSVAttributes(op))
3397 sv::setSVAttributes(reg, svAttrs);
3399 inputEdge.setValue(reg);
3400 (void)setLowering(op.getResult(),
reg);
3404LogicalResult FIRRTLLowering::visitDecl(RegResetOp op) {
3405 auto resultType =
lowerType(op.getResult().getType());
3408 if (resultType.isInteger(0))
3409 return setLowering(op.getResult(), Value());
3411 Value clockVal = getLoweredValue(op.getClockVal());
3412 Value resetSignal = getLoweredValue(op.getResetSignal());
3414 Value resetValue = getLoweredAndExtOrTruncValue(
3415 op.getResetValue(), type_cast<FIRRTLBaseType>(op.getResult().getType()));
3417 if (!clockVal || !resetSignal || !resetValue)
3421 auto innerSym = lowerInnerSymbol(op);
3422 bool isAsync = type_isa<AsyncResetType>(op.getResetSignal().getType());
3423 Backedge inputEdge = backedgeBuilder.
get(resultType);
3425 builder.create<seq::FirRegOp>(inputEdge, clockVal, op.getNameAttr(),
3426 resetSignal, resetValue, innerSym, isAsync);
3429 if (
auto randomRegister = op->getAttr(
"firrtl.random_init_register"))
3430 reg->setAttr(
"firrtl.random_init_register", randomRegister);
3431 if (
auto randomStart = op->getAttr(
"firrtl.random_init_start"))
3432 reg->setAttr(
"firrtl.random_init_start", randomStart);
3433 if (
auto randomEnd = op->getAttr(
"firrtl.random_init_end"))
3434 reg->setAttr(
"firrtl.random_init_end", randomEnd);
3437 if (
auto svAttrs = sv::getSVAttributes(op))
3438 sv::setSVAttributes(reg, svAttrs);
3440 inputEdge.setValue(reg);
3441 (void)setLowering(op.getResult(),
reg);
3446LogicalResult FIRRTLLowering::visitDecl(MemOp op) {
3449 if (type_isa<BundleType>(op.getDataType()))
3450 return op.emitOpError(
3451 "should have already been lowered from a ground type to an aggregate "
3452 "type using the LowerTypes pass. Use "
3453 "'firtool --lower-types' or 'circt-opt "
3454 "--pass-pipeline='firrtl.circuit(firrtl-lower-types)' "
3460 auto memType = seq::FirMemType::get(
3463 : std::optional<uint32_t>());
3465 seq::FirMemInitAttr memInit;
3466 if (
auto init = op.getInitAttr())
3467 memInit = seq::FirMemInitAttr::get(init.getContext(), init.getFilename(),
3468 init.getIsBinary(), init.getIsInline());
3470 auto memDecl = builder.create<seq::FirMemOp>(
3473 op.getInnerSymAttr(), memInit, op.getPrefixAttr(), Attribute{});
3477 if (!circuitState.isInDUT(theModule))
3478 if (
auto testBenchDir = circuitState.getTestBenchDirectory())
3479 memDecl.setOutputFileAttr(testBenchDir);
3483 for (
size_t i = 0, e = op.getNumResults(); i != e; ++i) {
3485 auto addOutput = [&](StringRef field,
size_t width, Value value) {
3488 (void)setLowering(a, value);
3494 auto addInput = [&](StringRef field, Value backedge) {
3496 if (cast<FIRRTLBaseType>(a.getType())
3498 .getBitWidthOrSentinel() > 0)
3499 (void)setLowering(a, backedge);
3505 auto addInputPort = [&](StringRef field,
size_t width) -> Value {
3509 Value backedge, portValue;
3511 portValue = getOrCreateXConstant(1);
3513 auto portType = IntegerType::get(op.getContext(), width);
3514 backedge = portValue = createBackedge(builder.getLoc(), portType);
3516 addInput(field, backedge);
3520 auto addClock = [&](StringRef field) -> Value {
3521 Type clockTy = seq::ClockType::get(op.getContext());
3522 Value portValue = createBackedge(builder.getLoc(), clockTy);
3523 addInput(field, portValue);
3527 auto memportKind = op.getPortKind(i);
3528 if (memportKind == MemOp::PortKind::Read) {
3529 auto addr = addInputPort(
"addr", op.getAddrBits());
3530 auto en = addInputPort(
"en", 1);
3531 auto clk = addClock(
"clk");
3532 auto data = builder.create<seq::FirMemReadOp>(memDecl,
addr,
clk,
en);
3534 }
else if (memportKind == MemOp::PortKind::ReadWrite) {
3535 auto addr = addInputPort(
"addr", op.getAddrBits());
3536 auto en = addInputPort(
"en", 1);
3537 auto clk = addClock(
"clk");
3540 auto mode = addInputPort(
"wmode", 1);
3542 mode = builder.createOrFold<
comb::AndOp>(mode, addInputPort(
"wmask", 1),
3549 auto rdata = builder.create<seq::FirMemReadWriteOp>(
3553 auto addr = addInputPort(
"addr", op.getAddrBits());
3556 auto en = addInputPort(
"en", 1);
3560 auto clk = addClock(
"clk");
3573LogicalResult FIRRTLLowering::visitDecl(InstanceOp oldInstance) {
3574 Operation *oldModule =
3575 oldInstance.getReferencedModule(circuitState.getInstanceGraph());
3577 auto *newModule = circuitState.getNewModule(oldModule);
3579 oldInstance->emitOpError(
"could not find module [")
3580 << oldInstance.getModuleName() <<
"] referenced by instance";
3586 ArrayAttr parameters;
3587 if (
auto oldExtModule = dyn_cast<FExtModuleOp>(oldModule))
3592 SmallVector<PortInfo, 8> portInfo = cast<FModuleLike>(oldModule).getPorts();
3597 for (
unsigned portIdx = 0, e = portInfo.size(); portIdx != e; ++portIdx)
3598 portIndicesByName[portInfo[portIdx].name] = portIdx;
3602 SmallVector<Value, 8> operands;
3603 for (
size_t portIndex = 0, e = portInfo.size(); portIndex != e; ++portIndex) {
3604 auto &port = portInfo[portIndex];
3607 oldInstance->emitOpError(
"could not lower type of port ") << port.name;
3612 if (portType.isInteger(0))
3616 if (port.isOutput())
3619 auto portResult = oldInstance.getResult(portIndex);
3620 assert(portResult &&
"invalid IR, couldn't find port");
3624 if (port.isInput()) {
3625 operands.push_back(createBackedge(portResult, portType));
3631 if (type_isa<AnalogType>(portResult.getType()) && portResult.hasOneUse()) {
3632 if (
auto attach = dyn_cast<AttachOp>(*portResult.getUsers().begin())) {
3634 auto loweredResult = getPossiblyInoutLoweredValue(source);
3635 operands.push_back(loweredResult);
3636 (void)setLowering(portResult, loweredResult);
3645 portType,
"." + port.getName().str() +
".wire");
3649 (void)setLowering(portResult, wire);
3651 operands.push_back(wire);
3658 auto innerSym = oldInstance.getInnerSymAttr();
3659 if (oldInstance.getLowerToBind()) {
3662 oldInstance.getContext(), oldInstance.getInnerSymAttr(), 0,
3665 auto bindOp = builder.create<sv::BindOp>(theModule.getNameAttr(),
3666 innerSym.getSymName());
3669 if (
auto outputFile = oldInstance->getAttr(
"output_file"))
3670 bindOp->setAttr(
"output_file", outputFile);
3673 circuitState.addBind(bindOp);
3677 auto newInstance = builder.create<hw::InstanceOp>(
3678 newModule, oldInstance.getNameAttr(), operands, parameters, innerSym);
3680 if (oldInstance.getLowerToBind() || oldInstance.getDoNotPrint())
3681 newInstance.setDoNotPrintAttr(builder.getUnitAttr());
3683 if (newInstance.getInnerSymAttr())
3684 if (
auto forceName = circuitState.instanceForceNames.lookup(
3685 {cast<hw::HWModuleOp>(newInstance->getParentOp()).getNameAttr(),
3686 newInstance.getInnerNameAttr()}))
3687 newInstance->setAttr(
"hw.verilogName", forceName);
3691 unsigned resultNo = 0;
3692 for (
size_t portIndex = 0, e = portInfo.size(); portIndex != e; ++portIndex) {
3693 auto &port = portInfo[portIndex];
3697 Value resultVal = newInstance.getResult(resultNo);
3699 auto oldPortResult = oldInstance.getResult(portIndex);
3700 (void)setLowering(oldPortResult, resultVal);
3706LogicalResult FIRRTLLowering::visitDecl(ContractOp oldOp) {
3707 SmallVector<Value> inputs;
3708 SmallVector<Type> types;
3709 for (
auto input : oldOp.getInputs()) {
3710 auto lowered = getLoweredValue(input);
3713 inputs.push_back(lowered);
3714 types.push_back(lowered.getType());
3717 auto newOp = builder.create<verif::ContractOp>(types, inputs);
3718 newOp->setDiscardableAttrs(oldOp->getDiscardableAttrDictionary());
3719 auto &body = newOp.getBody().emplaceBlock();
3721 for (
auto [newResult, oldResult, oldArg] :
3722 llvm::zip(newOp.getResults(), oldOp.getResults(),
3723 oldOp.getBody().getArguments())) {
3724 if (failed(setLowering(oldResult, newResult)))
3726 if (failed(setLowering(oldArg, newResult)))
3730 body.getOperations().splice(body.end(),
3731 oldOp.getBody().front().getOperations());
3732 addToWorklist(body);
3742LogicalResult FIRRTLLowering::lowerNoopCast(Operation *op) {
3743 auto operand = getPossiblyInoutLoweredValue(op->getOperand(0));
3748 return setLowering(op->getResult(0), operand);
3751LogicalResult FIRRTLLowering::visitExpr(AsSIntPrimOp op) {
3752 if (isa<ClockType>(op.getInput().getType()))
3753 return setLowering(op->getResult(0),
3754 getLoweredNonClockValue(op.getInput()));
3755 return lowerNoopCast(op);
3758LogicalResult FIRRTLLowering::visitExpr(AsUIntPrimOp op) {
3759 if (isa<ClockType>(op.getInput().getType()))
3760 return setLowering(op->getResult(0),
3761 getLoweredNonClockValue(op.getInput()));
3762 return lowerNoopCast(op);
3765LogicalResult FIRRTLLowering::visitExpr(AsClockPrimOp op) {
3766 return setLoweringTo<seq::ToClockOp>(op, getLoweredValue(op.getInput()));
3769LogicalResult FIRRTLLowering::visitUnrealizedConversionCast(
3770 mlir::UnrealizedConversionCastOp op) {
3772 if (op.getNumOperands() != 1 || op.getNumResults() != 1)
3775 auto operand = op.getOperand(0);
3776 auto result = op.getResult(0);
3779 if (type_isa<FIRRTLType>(operand.getType()) &&
3780 type_isa<FIRRTLType>(result.getType()))
3781 return lowerNoopCast(op);
3785 if (!type_isa<FIRRTLType>(operand.getType())) {
3786 if (type_isa<FIRRTLType>(result.getType()))
3787 return setLowering(result, getPossiblyInoutLoweredValue(operand));
3793 auto loweredResult = getLoweredValue(operand);
3794 if (!loweredResult) {
3797 if (operand.getType().isSignlessInteger(0)) {
3798 return setLowering(result, Value());
3805 result.replaceAllUsesWith(loweredResult);
3809LogicalResult FIRRTLLowering::visitExpr(HWStructCastOp op) {
3812 if (
auto opStructType = dyn_cast<hw::StructType>(op.getOperand().getType()))
3813 return setLowering(op, op.getOperand());
3817 auto result = getLoweredValue(op.getOperand());
3823 op.replaceAllUsesWith(result);
3827LogicalResult FIRRTLLowering::visitExpr(BitCastOp op) {
3828 auto operand = getLoweredValue(op.getOperand());
3831 auto resultType =
lowerType(op.getType());
3835 return setLoweringTo<hw::BitcastOp>(op, resultType, operand);
3838LogicalResult FIRRTLLowering::visitExpr(CvtPrimOp op) {
3839 auto operand = getLoweredValue(op.getOperand());
3843 if (type_cast<IntType>(op.getOperand().getType()).isUnsigned())
3844 return setLowering(op, getOrCreateIntConstant(1, 0));
3846 return setLowering(op, Value());
3851 if (type_cast<IntType>(op.getOperand().getType()).isSigned())
3852 return setLowering(op, operand);
3855 auto zero = getOrCreateIntConstant(1, 0);
3856 return setLoweringTo<comb::ConcatOp>(op, zero, operand);
3859LogicalResult FIRRTLLowering::visitExpr(NotPrimOp op) {
3860 auto operand = getLoweredValue(op.getInput());
3864 auto allOnes = getOrCreateIntConstant(
3865 APInt::getAllOnes(operand.getType().getIntOrFloatBitWidth()));
3866 return setLoweringTo<comb::XorOp>(op, operand, allOnes,
true);
3869LogicalResult FIRRTLLowering::visitExpr(NegPrimOp op) {
3872 auto operand = getLoweredAndExtendedValue(op.getInput(), op.getType());
3876 auto resultType =
lowerType(op.getType());
3878 auto zero = getOrCreateIntConstant(resultType.getIntOrFloatBitWidth(), 0);
3879 return setLoweringTo<comb::SubOp>(op, zero, operand,
true);
3883LogicalResult FIRRTLLowering::visitExpr(PadPrimOp op) {
3884 auto operand = getLoweredAndExtendedValue(op.getInput(), op.getType());
3887 return setLowering(op, operand);
3890LogicalResult FIRRTLLowering::visitExpr(XorRPrimOp op) {
3891 auto operand = getLoweredValue(op.getInput());
3894 return setLowering(op, getOrCreateIntConstant(1, 0));
3899 return setLoweringTo<comb::ParityOp>(op, builder.getIntegerType(1), operand,
3903LogicalResult FIRRTLLowering::visitExpr(AndRPrimOp op) {
3904 auto operand = getLoweredValue(op.getInput());
3907 return setLowering(op, getOrCreateIntConstant(1, 1));
3912 return setLoweringTo<comb::ICmpOp>(
3913 op, ICmpPredicate::eq, operand,
3914 getOrCreateIntConstant(
3915 APInt::getAllOnes(operand.getType().getIntOrFloatBitWidth())),
3919LogicalResult FIRRTLLowering::visitExpr(OrRPrimOp op) {
3920 auto operand = getLoweredValue(op.getInput());
3923 return setLowering(op, getOrCreateIntConstant(1, 0));
3929 return setLoweringTo<comb::ICmpOp>(
3930 op, ICmpPredicate::ne, operand,
3931 getOrCreateIntConstant(operand.getType().getIntOrFloatBitWidth(), 0),
3939template <
typename ResultOpType>
3940LogicalResult FIRRTLLowering::lowerBinOpToVariadic(Operation *op) {
3941 auto resultType = op->getResult(0).getType();
3942 auto lhs = getLoweredAndExtendedValue(op->getOperand(0), resultType);
3943 auto rhs = getLoweredAndExtendedValue(op->getOperand(1), resultType);
3947 return setLoweringTo<ResultOpType>(op, lhs, rhs,
true);
3953template <
typename ResultOpType>
3954LogicalResult FIRRTLLowering::lowerElementwiseLogicalOp(Operation *op) {
3955 auto resultType = op->getResult(0).getType();
3956 auto lhs = getLoweredAndExtendedValue(op->getOperand(0), resultType);
3957 auto rhs = getLoweredAndExtendedValue(op->getOperand(1), resultType);
3968 auto intType = builder.getIntegerType(*bitwidth);
3969 auto retType = lhs.getType();
3972 auto result = builder.createOrFold<ResultOpType>(lhs, rhs,
true);
3973 return setLoweringTo<hw::BitcastOp>(op, retType, result);
3978template <
typename ResultUn
signedOpType,
typename ResultSignedOpType>
3979LogicalResult FIRRTLLowering::lowerBinOp(Operation *op) {
3981 auto resultType = op->getResult(0).getType();
3982 auto lhs = getLoweredAndExtendedValue(op->getOperand(0), resultType);
3983 auto rhs = getLoweredAndExtendedValue(op->getOperand(1), resultType);
3988 if (type_cast<IntType>(resultType).isSigned())
3989 return setLoweringTo<ResultSignedOpType>(op, lhs, rhs,
true);
3990 return setLoweringTo<ResultUnsignedOpType>(op, lhs, rhs,
true);
3995LogicalResult FIRRTLLowering::lowerCmpOp(Operation *op, ICmpPredicate signedOp,
3996 ICmpPredicate unsignedOp) {
3998 auto lhsIntType = type_cast<IntType>(op->getOperand(0).getType());
3999 auto rhsIntType = type_cast<IntType>(op->getOperand(1).getType());
4000 if (!lhsIntType.hasWidth() || !rhsIntType.hasWidth())
4004 if (cmpType.getWidth() == 0)
4005 cmpType = UIntType::get(builder.getContext(), 1);
4006 auto lhs = getLoweredAndExtendedValue(op->getOperand(0), cmpType);
4007 auto rhs = getLoweredAndExtendedValue(op->getOperand(1), cmpType);
4012 Type resultType = builder.getIntegerType(1);
4013 return setLoweringTo<comb::ICmpOp>(
4014 op, resultType, lhsIntType.isSigned() ? signedOp : unsignedOp, lhs, rhs,
4020template <
typename SignedOp,
typename Un
signedOp>
4021LogicalResult FIRRTLLowering::lowerDivLikeOp(Operation *op) {
4025 auto opType = type_cast<IntType>(op->getResult(0).getType());
4026 if (opType.getWidth() == 0)
4027 return setLowering(op->getResult(0), Value());
4031 auto lhs = getLoweredAndExtendedValue(op->getOperand(0), resultType);
4032 auto rhs = getLoweredAndExtendedValue(op->getOperand(1), resultType);
4037 if (opType.isSigned())
4038 result = builder.createOrFold<SignedOp>(lhs, rhs,
true);
4040 result = builder.createOrFold<UnsignedOp>(lhs, rhs,
true);
4042 if (
auto *definingOp = result.getDefiningOp())
4045 if (resultType == opType)
4046 return setLowering(op->getResult(0), result);
4047 return setLoweringTo<comb::ExtractOp>(op,
lowerType(opType), result, 0);
4050LogicalResult FIRRTLLowering::visitExpr(CatPrimOp op) {
4052 if (op.getInputs().empty())
4053 return setLowering(op, Value());
4055 SmallVector<Value> loweredOperands;
4058 for (
auto operand : op.getInputs()) {
4059 auto loweredOperand = getLoweredValue(operand);
4060 if (loweredOperand) {
4061 loweredOperands.push_back(loweredOperand);
4064 auto result =
handleZeroBit(operand, [&]() {
return success(); });
4072 if (loweredOperands.empty())
4073 return setLowering(op, Value());
4076 return setLoweringTo<comb::ConcatOp>(op, loweredOperands);
4083LogicalResult FIRRTLLowering::visitExpr(IsXIntrinsicOp op) {
4084 auto input = getLoweredNonClockValue(op.getArg());
4088 if (!isa<IntType>(input.getType())) {
4089 auto srcType = op.getArg().getType();
4091 assert(bitwidth &&
"Unknown width");
4092 auto intType = builder.getIntegerType(*bitwidth);
4093 input = builder.createOrFold<
hw::BitcastOp>(intType, input);
4096 return setLoweringTo<comb::ICmpOp>(
4097 op, ICmpPredicate::ceq, input,
4098 getOrCreateXConstant(input.getType().getIntOrFloatBitWidth()),
true);
4101LogicalResult FIRRTLLowering::visitStmt(FPGAProbeIntrinsicOp op) {
4102 auto operand = getLoweredValue(op.getInput());
4103 builder.create<hw::WireOp>(operand);
4107LogicalResult FIRRTLLowering::visitExpr(PlusArgsTestIntrinsicOp op) {
4108 return setLoweringTo<sim::PlusArgsTestOp>(op, builder.getIntegerType(1),
4109 op.getFormatStringAttr());
4112LogicalResult FIRRTLLowering::visitExpr(PlusArgsValueIntrinsicOp op) {
4113 auto type =
lowerType(op.getResult().getType());
4117 auto valueOp = builder.create<sim::PlusArgsValueOp>(
4118 builder.getIntegerType(1), type, op.getFormatStringAttr());
4119 if (failed(setLowering(op.getResult(), valueOp.getResult())))
4121 if (failed(setLowering(op.getFound(), valueOp.getFound())))
4126LogicalResult FIRRTLLowering::visitExpr(SizeOfIntrinsicOp op) {
4127 op.emitError(
"SizeOf should have been resolved.");
4131LogicalResult FIRRTLLowering::visitExpr(ClockGateIntrinsicOp op) {
4133 if (op.getTestEnable())
4134 testEnable = getLoweredValue(op.getTestEnable());
4135 return setLoweringTo<seq::ClockGateOp>(
4136 op, getLoweredValue(op.getInput()), getLoweredValue(op.getEnable()),
4137 testEnable, hw::InnerSymAttr{});
4140LogicalResult FIRRTLLowering::visitExpr(ClockInverterIntrinsicOp op) {
4141 auto operand = getLoweredValue(op.getInput());
4142 return setLoweringTo<seq::ClockInverterOp>(op, operand);
4145LogicalResult FIRRTLLowering::visitExpr(ClockDividerIntrinsicOp op) {
4146 auto operand = getLoweredValue(op.getInput());
4147 return setLoweringTo<seq::ClockDividerOp>(op, operand, op.getPow2());
4150LogicalResult FIRRTLLowering::visitExpr(LTLAndIntrinsicOp op) {
4151 return setLoweringToLTL<ltl::AndOp>(
4153 ValueRange{getLoweredValue(op.getLhs()), getLoweredValue(op.getRhs())});
4156LogicalResult FIRRTLLowering::visitExpr(LTLOrIntrinsicOp op) {
4157 return setLoweringToLTL<ltl::OrOp>(
4159 ValueRange{getLoweredValue(op.getLhs()), getLoweredValue(op.getRhs())});
4162LogicalResult FIRRTLLowering::visitExpr(LTLIntersectIntrinsicOp op) {
4163 return setLoweringToLTL<ltl::IntersectOp>(
4165 ValueRange{getLoweredValue(op.getLhs()), getLoweredValue(op.getRhs())});
4168LogicalResult FIRRTLLowering::visitExpr(LTLDelayIntrinsicOp op) {
4169 return setLoweringToLTL<ltl::DelayOp>(op, getLoweredValue(op.getInput()),
4170 op.getDelayAttr(), op.getLengthAttr());
4173LogicalResult FIRRTLLowering::visitExpr(LTLConcatIntrinsicOp op) {
4174 return setLoweringToLTL<ltl::ConcatOp>(
4176 ValueRange{getLoweredValue(op.getLhs()), getLoweredValue(op.getRhs())});
4179LogicalResult FIRRTLLowering::visitExpr(LTLRepeatIntrinsicOp op) {
4180 return setLoweringToLTL<ltl::RepeatOp>(op, getLoweredValue(op.getInput()),
4181 op.getBaseAttr(), op.getMoreAttr());
4184LogicalResult FIRRTLLowering::visitExpr(LTLGoToRepeatIntrinsicOp op) {
4185 return setLoweringToLTL<ltl::GoToRepeatOp>(
4186 op, getLoweredValue(op.getInput()), op.getBaseAttr(), op.getMoreAttr());
4189LogicalResult FIRRTLLowering::visitExpr(LTLNonConsecutiveRepeatIntrinsicOp op) {
4190 return setLoweringToLTL<ltl::NonConsecutiveRepeatOp>(
4191 op, getLoweredValue(op.getInput()), op.getBaseAttr(), op.getMoreAttr());
4194LogicalResult FIRRTLLowering::visitExpr(LTLNotIntrinsicOp op) {
4195 return setLoweringToLTL<ltl::NotOp>(op, getLoweredValue(op.getInput()));
4198LogicalResult FIRRTLLowering::visitExpr(LTLImplicationIntrinsicOp op) {
4199 return setLoweringToLTL<ltl::ImplicationOp>(
4201 ValueRange{getLoweredValue(op.getLhs()), getLoweredValue(op.getRhs())});
4204LogicalResult FIRRTLLowering::visitExpr(LTLUntilIntrinsicOp op) {
4205 return setLoweringToLTL<ltl::UntilOp>(
4207 ValueRange{getLoweredValue(op.getLhs()), getLoweredValue(op.getRhs())});
4210LogicalResult FIRRTLLowering::visitExpr(LTLEventuallyIntrinsicOp op) {
4211 return setLoweringToLTL<ltl::EventuallyOp>(op,
4212 getLoweredValue(op.getInput()));
4215LogicalResult FIRRTLLowering::visitExpr(LTLClockIntrinsicOp op) {
4216 return setLoweringToLTL<ltl::ClockOp>(op, getLoweredValue(op.getInput()),
4217 ltl::ClockEdge::Pos,
4218 getLoweredNonClockValue(op.getClock()));
4221template <
typename TargetOp,
typename IntrinsicOp>
4222LogicalResult FIRRTLLowering::lowerVerifIntrinsicOp(IntrinsicOp op) {
4223 auto property = getLoweredValue(op.getProperty());
4224 auto enable = op.getEnable() ? getLoweredValue(op.getEnable()) : Value();
4225 builder.create<TargetOp>(property, enable, op.getLabelAttr());
4229LogicalResult FIRRTLLowering::visitStmt(VerifAssertIntrinsicOp op) {
4230 return lowerVerifIntrinsicOp<verif::AssertOp>(op);
4233LogicalResult FIRRTLLowering::visitStmt(VerifAssumeIntrinsicOp op) {
4234 return lowerVerifIntrinsicOp<verif::AssumeOp>(op);
4237LogicalResult FIRRTLLowering::visitStmt(VerifCoverIntrinsicOp op) {
4238 return lowerVerifIntrinsicOp<verif::CoverOp>(op);
4241LogicalResult FIRRTLLowering::visitStmt(VerifRequireIntrinsicOp op) {
4242 if (!isa<verif::ContractOp>(op->getParentOp()))
4243 return lowerVerifIntrinsicOp<verif::AssertOp>(op);
4244 return lowerVerifIntrinsicOp<verif::RequireOp>(op);
4247LogicalResult FIRRTLLowering::visitStmt(VerifEnsureIntrinsicOp op) {
4248 if (!isa<verif::ContractOp>(op->getParentOp()))
4249 return lowerVerifIntrinsicOp<verif::AssertOp>(op);
4250 return lowerVerifIntrinsicOp<verif::EnsureOp>(op);
4253LogicalResult FIRRTLLowering::visitExpr(HasBeenResetIntrinsicOp op) {
4254 auto clock = getLoweredNonClockValue(op.getClock());
4255 auto reset = getLoweredValue(op.getReset());
4256 if (!clock || !reset)
4258 auto resetType = op.getReset().getType();
4259 auto uintResetType = dyn_cast<UIntType>(resetType);
4260 auto isSync = uintResetType && uintResetType.getWidth() == 1;
4261 auto isAsync = isa<AsyncResetType>(resetType);
4262 if (!isAsync && !isSync) {
4263 auto d = op.emitError(
"uninferred reset passed to 'has_been_reset'; "
4264 "requires sync or async reset");
4265 d.attachNote() <<
"reset is of type " << resetType
4266 <<
", should be '!firrtl.uint<1>' or '!firrtl.asyncreset'";
4269 return setLoweringTo<verif::HasBeenResetOp>(op, clock, reset, isAsync);
4276LogicalResult FIRRTLLowering::visitExpr(BitsPrimOp op) {
4277 auto input = getLoweredValue(op.getInput());
4281 Type resultType = builder.getIntegerType(op.getHi() - op.getLo() + 1);
4282 return setLoweringTo<comb::ExtractOp>(op, resultType, input, op.getLo());
4285LogicalResult FIRRTLLowering::visitExpr(InvalidValueOp op) {
4286 auto resultTy =
lowerType(op.getType());
4293 if (type_isa<AnalogType>(op.getType()))
4296 return setLoweringTo<sv::WireOp>(op, resultTy,
".invalid_analog");
4299 if (type_cast<FIRRTLBaseType>(op.getType()).containsAnalog())
4310 auto constant = getOrCreateIntConstant(*bitwidth, 0);
4312 if (!type_isa<IntegerType>(resultTy))
4313 constant = builder.create<
hw::BitcastOp>(resultTy, constant);
4314 return setLowering(op, constant);
4318 op.emitOpError(
"unsupported type");
4322LogicalResult FIRRTLLowering::visitExpr(HeadPrimOp op) {
4323 auto input = getLoweredValue(op.getInput());
4326 auto inWidth = type_cast<IntegerType>(input.getType()).getWidth();
4327 if (op.getAmount() == 0)
4328 return setLowering(op, Value());
4329 Type resultType = builder.getIntegerType(op.getAmount());
4330 return setLoweringTo<comb::ExtractOp>(op, resultType, input,
4331 inWidth - op.getAmount());
4334LogicalResult FIRRTLLowering::visitExpr(ShlPrimOp op) {
4335 auto input = getLoweredValue(op.getInput());
4338 if (op.getAmount() == 0)
4340 return setLowering(op, getOrCreateIntConstant(op.getAmount(), 0));
4345 if (op.getAmount() == 0)
4346 return setLowering(op, input);
4348 auto zero = getOrCreateIntConstant(op.getAmount(), 0);
4349 return setLoweringTo<comb::ConcatOp>(op, input, zero);
4352LogicalResult FIRRTLLowering::visitExpr(ShrPrimOp op) {
4353 auto input = getLoweredValue(op.getInput());
4358 auto inWidth = type_cast<IntegerType>(input.getType()).getWidth();
4359 auto shiftAmount = op.getAmount();
4360 if (shiftAmount >= inWidth) {
4362 if (type_cast<IntType>(op.getInput().getType()).isUnsigned())
4363 return setLowering(op, {});
4366 shiftAmount = inWidth - 1;
4369 Type resultType = builder.getIntegerType(inWidth - shiftAmount);
4370 return setLoweringTo<comb::ExtractOp>(op, resultType, input, shiftAmount);
4373LogicalResult FIRRTLLowering::visitExpr(TailPrimOp op) {
4374 auto input = getLoweredValue(op.getInput());
4378 auto inWidth = type_cast<IntegerType>(input.getType()).getWidth();
4379 if (inWidth == op.getAmount())
4380 return setLowering(op, Value());
4381 Type resultType = builder.getIntegerType(inWidth - op.getAmount());
4382 return setLoweringTo<comb::ExtractOp>(op, resultType, input, 0);
4385LogicalResult FIRRTLLowering::visitExpr(MuxPrimOp op) {
4386 auto cond = getLoweredValue(op.getSel());
4387 auto ifTrue = getLoweredAndExtendedValue(op.getHigh(), op.getType());
4388 auto ifFalse = getLoweredAndExtendedValue(op.getLow(), op.getType());
4389 if (!cond || !ifTrue || !ifFalse)
4392 if (isa<ClockType>(op.getType()))
4393 return setLoweringTo<seq::ClockMuxOp>(op, cond, ifTrue, ifFalse);
4394 return setLoweringTo<comb::MuxOp>(op, ifTrue.getType(), cond, ifTrue, ifFalse,
4398LogicalResult FIRRTLLowering::visitExpr(Mux2CellIntrinsicOp op) {
4399 auto cond = getLoweredValue(op.getSel());
4400 auto ifTrue = getLoweredAndExtendedValue(op.getHigh(), op.getType());
4401 auto ifFalse = getLoweredAndExtendedValue(op.getLow(), op.getType());
4402 if (!cond || !ifTrue || !ifFalse)
4405 auto val = builder.create<
comb::MuxOp>(ifTrue.getType(), cond, ifTrue,
4407 return setLowering(op, createValueWithMuxAnnotation(val,
true));
4410LogicalResult FIRRTLLowering::visitExpr(Mux4CellIntrinsicOp op) {
4411 auto sel = getLoweredValue(op.getSel());
4412 auto v3 = getLoweredAndExtendedValue(op.getV3(), op.getType());
4413 auto v2 = getLoweredAndExtendedValue(op.getV2(), op.getType());
4414 auto v1 = getLoweredAndExtendedValue(op.getV1(), op.getType());
4415 auto v0 = getLoweredAndExtendedValue(op.getV0(), op.getType());
4416 if (!sel || !v3 || !v2 || !v1 || !v0)
4418 Value array[] = {v3, v2, v1, v0};
4421 return setLowering(op, createValueWithMuxAnnotation(val,
false));
4440Value FIRRTLLowering::createValueWithMuxAnnotation(Operation *op,
bool isMux2) {
4441 assert(op->getNumResults() == 1 &&
"only expect a single result");
4442 auto val = op->getResult(0);
4446 op, sv::SVAttributeAttr::get(builder.getContext(),
"cadence map_to_mux",
4453 OpBuilder::InsertionGuard guard(builder);
4454 builder.setInsertionPoint(op);
4455 StringRef namehint = isMux2 ?
"mux2cell_in" :
"mux4cell_in";
4456 for (
auto [idx, operand] :
llvm::enumerate(op->getOperands())) {
4458 op->getContext(),
nullptr, 0,
4461 builder.create<hw::WireOp>(operand, namehint + Twine(idx), innerSym);
4462 op->setOperand(idx, wire);
4466 auto assignOp = builder.create<
sv::AssignOp>(valWire, val);
4467 sv::setSVAttributes(assignOp,
4468 sv::SVAttributeAttr::get(builder.getContext(),
4469 "synopsys infer_mux_override",
4474Value FIRRTLLowering::createArrayIndexing(Value array, Value index) {
4476 auto size = hw::type_cast<hw::ArrayType>(array.getType()).getNumElements();
4481 if (!llvm::isPowerOf2_64(size)) {
4482 auto extElem = getOrCreateIntConstant(APInt(llvm::Log2_64_Ceil(size), 0));
4484 SmallVector<Value> temp(llvm::NextPowerOf2(size) - size, extValue);
4486 Value temp2[] = {ext.getResult(), array};
4492 return inBoundsRead;
4495LogicalResult FIRRTLLowering::visitExpr(MultibitMuxOp op) {
4497 auto index = getLoweredAndExtOrTruncValue(
4499 UIntType::get(op.getContext(),
4504 SmallVector<Value> loweredInputs;
4505 loweredInputs.reserve(op.getInputs().size());
4506 for (
auto input : op.getInputs()) {
4507 auto lowered = getLoweredAndExtendedValue(input, op.getType());
4510 loweredInputs.push_back(lowered);
4514 return setLowering(op, createArrayIndexing(array, index));
4517LogicalResult FIRRTLLowering::visitExpr(VerbatimExprOp op) {
4518 auto resultTy =
lowerType(op.getType());
4522 SmallVector<Value, 4> operands;
4523 operands.reserve(op.getSubstitutions().size());
4524 for (
auto operand : op.getSubstitutions()) {
4525 auto lowered = getLoweredValue(operand);
4528 operands.push_back(lowered);
4531 ArrayAttr symbols = op.getSymbolsAttr();
4533 symbols = ArrayAttr::get(op.getContext(), {});
4535 return setLoweringTo<sv::VerbatimExprOp>(op, resultTy, op.getTextAttr(),
4539LogicalResult FIRRTLLowering::visitExpr(XMRRefOp op) {
4543 Type baseType = op.getType().getType();
4546 if (isa<ClockType>(baseType))
4547 xmrType = builder.getIntegerType(1);
4551 return setLoweringTo<sv::XMRRefOp>(op, sv::InOutType::get(xmrType),
4552 op.getRef(), op.getVerbatimSuffixAttr());
4555LogicalResult FIRRTLLowering::visitExpr(XMRDerefOp op) {
4559 if (isa<ClockType>(op.getType()))
4560 xmrType = builder.getIntegerType(1);
4564 auto xmr = builder.create<sv::XMRRefOp>(
4565 sv::InOutType::get(xmrType), op.getRef(), op.getVerbatimSuffixAttr());
4566 auto readXmr = getReadValue(xmr);
4567 if (!isa<ClockType>(op.getType()))
4568 return setLowering(op, readXmr);
4569 return setLoweringTo<seq::ToClockOp>(op, readXmr);
4574LogicalResult FIRRTLLowering::visitExpr(TimeOp op) {
return success(); }
4575LogicalResult FIRRTLLowering::visitExpr(HierarchicalModuleNameOp op) {
4583LogicalResult FIRRTLLowering::visitStmt(SkipOp op) {
4595FailureOr<bool> FIRRTLLowering::lowerConnect(Value destVal, Value srcVal) {
4596 auto srcType = srcVal.getType();
4597 auto dstType = destVal.getType();
4598 if (srcType != dstType &&
4599 (isa<hw::TypeAliasType>(srcType) || isa<hw::TypeAliasType>(dstType))) {
4600 srcVal = builder.create<
hw::BitcastOp>(destVal.getType(), srcVal);
4602 return TypeSwitch<Operation *, FailureOr<bool>>(destVal.getDefiningOp())
4603 .Case<hw::WireOp>([&](
auto op) {
4604 maybeUnused(op.getInput());
4605 op.getInputMutable().assign(srcVal);
4608 .Case<seq::FirRegOp>([&](
auto op) {
4609 maybeUnused(op.getNext());
4610 op.getNextMutable().assign(srcVal);
4613 .Case<hw::StructExtractOp, hw::ArrayGetOp>([](
auto op) {
4616 op.emitOpError(
"used as connect destination");
4619 .Default([](
auto) {
return false; });
4622LogicalResult FIRRTLLowering::visitStmt(ConnectOp op) {
4623 auto dest = op.getDest();
4625 auto destType = type_cast<FIRRTLBaseType>(dest.getType()).getPassiveType();
4626 auto srcVal = getLoweredAndExtendedValue(op.getSrc(), destType);
4628 return handleZeroBit(op.getSrc(), []() { return success(); });
4630 auto destVal = getPossiblyInoutLoweredValue(dest);
4634 auto result = lowerConnect(destVal, srcVal);
4642 if (updateIfBackedge(destVal, srcVal))
4645 if (!isa<hw::InOutType>(destVal.getType()))
4646 return op.emitError(
"destination isn't an inout type");
4652LogicalResult FIRRTLLowering::visitStmt(MatchingConnectOp op) {
4653 auto dest = op.getDest();
4654 auto srcVal = getLoweredValue(op.getSrc());
4656 return handleZeroBit(op.getSrc(), []() { return success(); });
4658 auto destVal = getPossiblyInoutLoweredValue(dest);
4662 auto result = lowerConnect(destVal, srcVal);
4670 if (updateIfBackedge(destVal, srcVal))
4673 if (!isa<hw::InOutType>(destVal.getType()))
4674 return op.emitError(
"destination isn't an inout type");
4680LogicalResult FIRRTLLowering::visitStmt(ForceOp op) {
4681 auto srcVal = getLoweredValue(op.getSrc());
4685 auto destVal = getPossiblyInoutLoweredValue(op.getDest());
4689 if (!isa<hw::InOutType>(destVal.getType()))
4690 return op.emitError(
"destination isn't an inout type");
4693 circuitState.addMacroDecl(builder.getStringAttr(
"SYNTHESIS"));
4694 addToIfDefBlock(
"SYNTHESIS", std::function<
void()>(), [&]() {
4695 addToInitialBlock([&]() { builder.create<sv::ForceOp>(destVal, srcVal); });
4700LogicalResult FIRRTLLowering::visitStmt(RefForceOp op) {
4701 auto src = getLoweredNonClockValue(op.getSrc());
4702 auto clock = getLoweredNonClockValue(op.getClock());
4703 auto pred = getLoweredValue(op.getPredicate());
4704 if (!src || !clock || !pred)
4707 auto destVal = getPossiblyInoutLoweredValue(op.getDest());
4712 circuitState.addMacroDecl(builder.getStringAttr(
"SYNTHESIS"));
4713 addToIfDefBlock(
"SYNTHESIS", std::function<
void()>(), [&]() {
4714 addToAlwaysBlock(clock, [&]() {
4715 addIfProceduralBlock(
4716 pred, [&]() { builder.create<sv::ForceOp>(destVal, src); });
4721LogicalResult FIRRTLLowering::visitStmt(RefForceInitialOp op) {
4722 auto src = getLoweredNonClockValue(op.getSrc());
4723 auto pred = getLoweredValue(op.getPredicate());
4727 auto destVal = getPossiblyInoutLoweredValue(op.getDest());
4732 circuitState.addMacroDecl(builder.getStringAttr(
"SYNTHESIS"));
4733 addToIfDefBlock(
"SYNTHESIS", std::function<
void()>(), [&]() {
4734 addToInitialBlock([&]() {
4735 addIfProceduralBlock(
4736 pred, [&]() { builder.create<sv::ForceOp>(destVal, src); });
4741LogicalResult FIRRTLLowering::visitStmt(RefReleaseOp op) {
4742 auto clock = getLoweredNonClockValue(op.getClock());
4743 auto pred = getLoweredValue(op.getPredicate());
4744 if (!clock || !pred)
4747 auto destVal = getPossiblyInoutLoweredValue(op.getDest());
4752 circuitState.addMacroDecl(builder.getStringAttr(
"SYNTHESIS"));
4753 addToIfDefBlock(
"SYNTHESIS", std::function<
void()>(), [&]() {
4754 addToAlwaysBlock(clock, [&]() {
4755 addIfProceduralBlock(pred,
4756 [&]() { builder.create<sv::ReleaseOp>(destVal); });
4761LogicalResult FIRRTLLowering::visitStmt(RefReleaseInitialOp op) {
4762 auto destVal = getPossiblyInoutLoweredValue(op.getDest());
4763 auto pred = getLoweredValue(op.getPredicate());
4764 if (!destVal || !pred)
4768 circuitState.addMacroDecl(builder.getStringAttr(
"SYNTHESIS"));
4769 addToIfDefBlock(
"SYNTHESIS", std::function<
void()>(), [&]() {
4770 addToInitialBlock([&]() {
4771 addIfProceduralBlock(pred,
4772 [&]() { builder.create<sv::ReleaseOp>(destVal); });
4780 StringRef originalFormatString,
4781 mlir::OperandRange operands,
4782 StringAttr &result) {
4785 SmallString<32> formatString;
4786 for (
size_t i = 0, e = originalFormatString.size(), subIdx = 0; i != e; ++i) {
4787 char c = originalFormatString[i];
4791 formatString.push_back(c);
4794 SmallString<6> width;
4795 c = originalFormatString[++i];
4798 c = originalFormatString[++i];
4809 formatString.append(width);
4815 formatString.push_back(c);
4822 if (originalFormatString.slice(i, i + 4) !=
"{{}}") {
4823 formatString.push_back(c);
4827 auto substitution = operands[subIdx++];
4828 assert(type_isa<FStringType>(substitution.getType()) &&
4829 "the operand for a '{{}}' substitution must be an 'fstring' type");
4831 TypeSwitch<Operation *, LogicalResult>(substitution.getDefiningOp())
4832 .template Case<TimeOp>([&](
auto) {
4833 formatString.append(
"%0t");
4836 .
template Case<HierarchicalModuleNameOp>([&](
auto) {
4837 formatString.append(
"%m");
4840 .Default([&](
auto) {
4841 emitError(loc,
"has a substitution with an unimplemented "
4843 .attachNote(substitution.getLoc())
4844 <<
"op with an unimplemented lowering is here";
4854 formatString.push_back(c);
4858 result = StringAttr::get(loc->getContext(), formatString);
4865LogicalResult FIRRTLLowering::visitPrintfLike(
4866 T op,
const FileDescriptorInfo &fileDescriptorInfo,
bool usePrintfCond) {
4867 auto clock = getLoweredNonClockValue(op.getClock());
4868 auto cond = getLoweredValue(op.getCond());
4869 if (!clock || !cond)
4872 StringAttr formatString;
4874 op.getSubstitutions(), formatString)))
4877 auto fn = [&](Value fd) {
4878 SmallVector<Value> operands;
4879 if (failed(loweredFmtOperands(op.getSubstitutions(), operands)))
4881 builder.create<sv::FWriteOp>(op.getLoc(), fd, formatString, operands);
4885 return lowerStatementWithFd(fileDescriptorInfo, clock, cond, fn,
4889LogicalResult FIRRTLLowering::visitStmt(FPrintFOp op) {
4890 StringAttr outputFileAttr;
4892 op.getOutputFileSubstitutions(),
4896 FileDescriptorInfo outputFile(outputFileAttr,
4897 op.getOutputFileSubstitutions());
4898 return visitPrintfLike(op, outputFile,
false);
4902LogicalResult FIRRTLLowering::visitStmt(FFlushOp op) {
4903 auto clock = getLoweredNonClockValue(op.getClock());
4904 auto cond = getLoweredValue(op.getCond());
4905 if (!clock || !cond)
4908 auto fn = [&](Value fd) {
4909 builder.create<sv::FFlushOp>(op.getLoc(), fd);
4913 if (!op.getOutputFileAttr())
4914 return lowerStatementWithFd({}, clock, cond, fn,
false);
4918 StringAttr outputFileAttr;
4920 op.getOutputFileSubstitutions(),
4924 return lowerStatementWithFd(
4925 FileDescriptorInfo(outputFileAttr, op.getOutputFileSubstitutions()),
4926 clock, cond, fn,
false);
4931LogicalResult FIRRTLLowering::visitStmt(StopOp op) {
4932 auto clock = getLoweredValue(op.getClock());
4933 auto cond = getLoweredValue(op.getCond());
4934 if (!clock || !cond)
4937 circuitState.usedStopCond =
true;
4938 circuitState.addFragment(theModule,
"STOP_COND_FRAGMENT");
4941 builder.create<sv::MacroRefExprOp>(cond.getType(),
"STOP_COND_");
4942 Value exitCond = builder.createOrFold<
comb::AndOp>(stopCond, cond,
true);
4944 if (op.getExitCode())
4945 builder.create<sim::FatalOp>(clock, exitCond);
4947 builder.create<sim::FinishOp>(clock, exitCond);
4955template <
typename... Args>
4957 StringRef opName, Args &&...args) {
4958 if (opName ==
"assert")
4959 return builder.create<sv::AssertOp>(std::forward<Args>(args)...);
4960 if (opName ==
"assume")
4961 return builder.create<sv::AssumeOp>(std::forward<Args>(args)...);
4962 if (opName ==
"cover")
4963 return builder.create<sv::CoverOp>(std::forward<Args>(args)...);
4964 llvm_unreachable(
"unknown verification op");
4970template <
typename... Args>
4972 StringRef opName, Args &&...args) {
4973 if (opName ==
"assert")
4974 return builder.create<sv::AssertConcurrentOp>(std::forward<Args>(args)...);
4975 if (opName ==
"assume")
4976 return builder.create<sv::AssumeConcurrentOp>(std::forward<Args>(args)...);
4977 if (opName ==
"cover")
4978 return builder.create<sv::CoverConcurrentOp>(std::forward<Args>(args)...);
4979 llvm_unreachable(
"unknown verification op");
5000LogicalResult FIRRTLLowering::lowerVerificationStatement(
5001 Operation *op, StringRef labelPrefix, Value opClock, Value opPredicate,
5002 Value opEnable, StringAttr opMessageAttr, ValueRange opOperands,
5003 StringAttr opNameAttr,
bool isConcurrent, EventControl opEventControl) {
5004 StringRef opName = op->getName().stripDialect();
5007 ArrayRef<Attribute> guards{};
5008 if (
auto guardsAttr = op->template getAttrOfType<ArrayAttr>(
"guards"))
5009 guards = guardsAttr.getValue();
5011 auto isCover = isa<CoverOp>(op);
5012 auto clock = getLoweredNonClockValue(opClock);
5013 auto enable = getLoweredValue(opEnable);
5014 auto predicate = getLoweredValue(opPredicate);
5015 if (!clock || !enable || !predicate)
5019 if (opNameAttr && !opNameAttr.getValue().empty())
5021 StringAttr prefixedLabel;
5024 StringAttr::get(builder.getContext(), labelPrefix + label.getValue());
5027 SmallVector<Value> messageOps;
5031 if (flavor == VerificationFlavor::IfElseFatal && !isa<AssertOp>(op))
5032 flavor = VerificationFlavor::None;
5034 if (flavor == VerificationFlavor::None) {
5038 auto format = op->getAttrOfType<StringAttr>(
"format");
5040 if (isConcurrent && format && format.getValue() ==
"ifElseFatal") {
5041 if (!isa<AssertOp>(op))
5042 return op->emitError()
5043 <<
"ifElseFatal format cannot be used for non-assertions";
5044 flavor = VerificationFlavor::IfElseFatal;
5045 }
else if (isConcurrent)
5046 flavor = VerificationFlavor::SVA;
5048 flavor = VerificationFlavor::Immediate;
5051 if (!isCover && opMessageAttr && !opMessageAttr.getValue().empty()) {
5052 message = opMessageAttr;
5053 if (failed(loweredFmtOperands(opOperands, messageOps)))
5056 if (flavor == VerificationFlavor::SVA) {
5061 for (
auto &loweredValue : messageOps)
5062 loweredValue = builder.create<
sv::SampledOp>(loweredValue);
5068 case VerificationFlavor::Immediate: {
5070 auto deferImmediate = circt::sv::DeferAssertAttr::get(
5071 builder.getContext(), circt::sv::DeferAssert::Immediate);
5072 addToAlwaysBlock(clock, [&]() {
5073 addIfProceduralBlock(enable, [&]() {
5075 prefixedLabel, message, messageOps);
5080 case VerificationFlavor::IfElseFatal: {
5081 assert(isa<AssertOp>(op) &&
"only assert is expected");
5084 auto boolType = IntegerType::get(builder.getContext(), 1);
5085 predicate = comb::createOrFoldNot(predicate, builder,
true);
5086 predicate = builder.createOrFold<
comb::AndOp>(enable, predicate,
true);
5088 circuitState.addMacroDecl(builder.getStringAttr(
"SYNTHESIS"));
5089 addToIfDefBlock(
"SYNTHESIS", {}, [&]() {
5090 addToAlwaysBlock(clock, [&]() {
5091 addIfProceduralBlock(predicate, [&]() {
5092 circuitState.usedStopCond =
true;
5093 circuitState.addFragment(theModule,
"STOP_COND_FRAGMENT");
5095 circuitState.usedAssertVerboseCond =
true;
5096 circuitState.addFragment(theModule,
"ASSERT_VERBOSE_COND_FRAGMENT");
5098 addIfProceduralBlock(
5099 builder.create<sv::MacroRefExprOp>(boolType,
5100 "ASSERT_VERBOSE_COND_"),
5101 [&]() { builder.create<sv::ErrorOp>(message, messageOps); });
5102 addIfProceduralBlock(
5103 builder.create<sv::MacroRefExprOp>(boolType,
"STOP_COND_"),
5104 [&]() { builder.create<sv::FatalOp>(); });
5110 case VerificationFlavor::SVA: {
5115 comb::createOrFoldNot(enable, builder,
true);
5117 builder.createOrFold<
comb::OrOp>(notEnable, predicate,
true);
5119 predicate = builder.createOrFold<
comb::AndOp>(enable, predicate,
true);
5123 sv::EventControl event;
5124 switch (opEventControl) {
5125 case EventControl::AtPosEdge:
5126 event = circt::sv::EventControl::AtPosEdge;
5128 case EventControl::AtEdge:
5129 event = circt::sv::EventControl::AtEdge;
5131 case EventControl::AtNegEdge:
5132 event = circt::sv::EventControl::AtNegEdge;
5138 circt::sv::EventControlAttr::get(builder.getContext(), event), clock,
5139 predicate, prefixedLabel, message, messageOps);
5142 case VerificationFlavor::None:
5144 "flavor `None` must be converted into one of concreate flavors");
5151 return emitGuards(op->getLoc(), guards,
emit);
5155LogicalResult FIRRTLLowering::visitStmt(AssertOp op) {
5156 return lowerVerificationStatement(
5157 op,
"assert__", op.getClock(), op.getPredicate(), op.getEnable(),
5158 op.getMessageAttr(), op.getSubstitutions(), op.getNameAttr(),
5159 op.getIsConcurrent(), op.getEventControl());
5163LogicalResult FIRRTLLowering::visitStmt(AssumeOp op) {
5164 return lowerVerificationStatement(
5165 op,
"assume__", op.getClock(), op.getPredicate(), op.getEnable(),
5166 op.getMessageAttr(), op.getSubstitutions(), op.getNameAttr(),
5167 op.getIsConcurrent(), op.getEventControl());
5171LogicalResult FIRRTLLowering::visitStmt(CoverOp op) {
5172 return lowerVerificationStatement(
5173 op,
"cover__", op.getClock(), op.getPredicate(), op.getEnable(),
5174 op.getMessageAttr(), op.getSubstitutions(), op.getNameAttr(),
5175 op.getIsConcurrent(), op.getEventControl());
5179LogicalResult FIRRTLLowering::visitStmt(UnclockedAssumeIntrinsicOp op) {
5184 auto guardsAttr = op->getAttrOfType<mlir::ArrayAttr>(
"guards");
5185 ArrayRef<Attribute> guards =
5186 guardsAttr ? guardsAttr.getValue() : ArrayRef<Attribute>();
5188 auto label = op.getNameAttr();
5189 StringAttr assumeLabel;
5190 if (label && !label.empty())
5192 StringAttr::get(builder.getContext(),
"assume__" + label.getValue());
5193 auto predicate = getLoweredValue(op.getPredicate());
5194 auto enable = getLoweredValue(op.getEnable());
5195 auto notEnable = comb::createOrFoldNot(enable, builder,
true);
5196 predicate = builder.createOrFold<
comb::OrOp>(notEnable, predicate,
true);
5198 SmallVector<Value> messageOps;
5199 for (
auto operand : op.getSubstitutions()) {
5200 auto loweredValue = getLoweredValue(operand);
5201 if (!loweredValue) {
5205 loweredValue = getOrCreateIntConstant(1, 0);
5207 messageOps.push_back(loweredValue);
5209 return emitGuards(op.getLoc(), guards, [&]() {
5210 builder.create<sv::AlwaysOp>(
5211 ArrayRef(sv::EventControl::AtEdge), ArrayRef(predicate), [&]() {
5212 if (op.getMessageAttr().getValue().empty())
5213 buildImmediateVerifOp(
5214 builder,
"assume", predicate,
5215 circt::sv::DeferAssertAttr::get(
5216 builder.getContext(), circt::sv::DeferAssert::Immediate),
5219 buildImmediateVerifOp(
5220 builder,
"assume", predicate,
5221 circt::sv::DeferAssertAttr::get(
5222 builder.getContext(), circt::sv::DeferAssert::Immediate),
5223 assumeLabel, op.getMessageAttr(), messageOps);
5228LogicalResult FIRRTLLowering::visitStmt(AttachOp op) {
5230 if (op.getAttached().size() < 2)
5233 SmallVector<Value, 4> inoutValues;
5234 for (
auto v : op.getAttached()) {
5235 inoutValues.push_back(getPossiblyInoutLoweredValue(v));
5236 if (!inoutValues.back()) {
5240 inoutValues.pop_back();
5244 if (!isa<hw::InOutType>(inoutValues.back().getType()))
5245 return op.emitError(
"operand isn't an inout type");
5248 if (inoutValues.size() < 2)
5259 bool isAttachInternalOnly =
5260 llvm::none_of(inoutValues, [](
auto v) {
return isa<BlockArgument>(v); });
5262 if (isAttachInternalOnly) {
5263 auto v0 = inoutValues.front();
5264 for (
auto v : inoutValues) {
5267 v.replaceAllUsesWith(v0);
5274 circuitState.addMacroDecl(builder.getStringAttr(
"SYNTHESIS"));
5275 circuitState.addMacroDecl(builder.getStringAttr(
"VERILATOR"));
5280 SmallVector<Value, 4> values;
5281 for (
auto inoutValue : inoutValues)
5282 values.push_back(getReadValue(inoutValue));
5284 for (
size_t i1 = 0, e = inoutValues.size(); i1 != e; ++i1) {
5285 for (
size_t i2 = 0; i2 != e; ++i2)
5287 builder.create<
sv::AssignOp>(inoutValues[i1], values[i2]);
5296 builder.create<sv::VerbatimOp>(
5297 "`error \"Verilator does not support alias and thus "
5299 "arbitrarily connect bidirectional wires and ports\"");
5301 [&]() { builder.create<sv::AliasOp>(inoutValues); });
5307LogicalResult FIRRTLLowering::visitStmt(BindOp op) {
5308 builder.create<sv::BindOp>(op.getInstanceAttr());
5312LogicalResult FIRRTLLowering::fixupLTLOps() {
5313 if (ltlOpFixupWorklist.empty())
5315 LLVM_DEBUG(llvm::dbgs() <<
"Fixing up " << ltlOpFixupWorklist.size()
5319 for (
unsigned i = 0, e = ltlOpFixupWorklist.size(); i != e; ++i)
5320 for (
auto *user : ltlOpFixupWorklist[i]->getUsers())
5321 if (isa<
hw::WireOp>(user))
5322 ltlOpFixupWorklist.insert(user);
5325 while (!ltlOpFixupWorklist.empty()) {
5326 auto *op = ltlOpFixupWorklist.pop_back_val();
5329 if (
auto opIntf = dyn_cast_or_null<mlir::InferTypeOpInterface>(op)) {
5330 LLVM_DEBUG(llvm::dbgs() <<
"- Update " << *op <<
"\n");
5331 SmallVector<Type, 2> types;
5332 auto result = opIntf.inferReturnTypes(
5333 op->getContext(), op->getLoc(), op->getOperands(),
5334 op->getAttrDictionary(), op->getPropertiesStorage(), op->getRegions(),
5338 assert(types.size() == op->getNumResults());
5342 for (
auto [result, type] :
llvm::zip(op->getResults(), types)) {
5343 if (result.getType() == type)
5345 LLVM_DEBUG(llvm::dbgs()
5346 <<
" - Result #" << result.getResultNumber() <<
" from "
5347 << result.getType() <<
" to " << type <<
"\n");
5348 result.setType(type);
5349 for (
auto *user : result.getUsers())
5351 ltlOpFixupWorklist.insert(user);
5356 if (
auto wireOp = dyn_cast<hw::WireOp>(op)) {
5357 if (isa<ltl::SequenceType, ltl::PropertyType>(wireOp.getType())) {
5358 wireOp.replaceAllUsesWith(wireOp.getInput());
5359 LLVM_DEBUG(llvm::dbgs() <<
"- Remove " << wireOp <<
"\n");
5360 if (wireOp.use_empty())
5367 SmallPtrSet<Operation *, 4> usersReported;
5368 for (
auto *user : op->getUsers()) {
5369 if (!usersReported.insert(user).second)
5371 if (isa<ltl::LTLDialect, verif::VerifDialect>(user->getDialect()))
5373 if (isa<hw::WireOp>(user))
5375 auto d = op->emitError(
5376 "verification operation used in a non-verification context");
5377 d.attachNote(user->getLoc())
5378 <<
"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