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)
912 global_id[name] = $fopen(name);
913 return global_id[name];
919 b.create<sv::MacroDefOp>("__CIRCT_LIB_LOGGING",
"");
924 if (state.usedPrintf) {
925 b.create<sv::MacroDeclOp>(
"PRINTF_COND");
926 b.create<sv::MacroDeclOp>(
"PRINTF_COND_");
927 b.create<emit::FragmentOp>(
"PRINTF_COND_FRAGMENT", [&] {
928 b.create<sv::VerbatimOp>(
929 "\n// Users can define 'PRINTF_COND' to add an extra gate to "
931 emitGuard(
"PRINTF_COND_", [&]() {
932 emitGuardedDefine(
"PRINTF_COND",
"PRINTF_COND_",
"(`PRINTF_COND)",
"1");
937 if (state.usedAssertVerboseCond) {
938 b.create<sv::MacroDeclOp>(
"ASSERT_VERBOSE_COND");
939 b.create<sv::MacroDeclOp>(
"ASSERT_VERBOSE_COND_");
940 b.create<emit::FragmentOp>(
"ASSERT_VERBOSE_COND_FRAGMENT", [&] {
941 b.create<sv::VerbatimOp>(
942 "\n// Users can define 'ASSERT_VERBOSE_COND' to add an extra "
943 "gate to assert error printing.");
944 emitGuard(
"ASSERT_VERBOSE_COND_", [&]() {
945 emitGuardedDefine(
"ASSERT_VERBOSE_COND",
"ASSERT_VERBOSE_COND_",
946 "(`ASSERT_VERBOSE_COND)",
"1");
951 if (state.usedStopCond) {
952 b.create<sv::MacroDeclOp>(
"STOP_COND");
953 b.create<sv::MacroDeclOp>(
"STOP_COND_");
954 b.create<emit::FragmentOp>(
"STOP_COND_FRAGMENT", [&] {
955 b.create<sv::VerbatimOp>(
956 "\n// Users can define 'STOP_COND' to add an extra gate "
957 "to stop conditions.");
958 emitGuard(
"STOP_COND_", [&]() {
959 emitGuardedDefine(
"STOP_COND",
"STOP_COND_",
"(`STOP_COND)",
"1");
966FIRRTLModuleLowering::lowerPorts(ArrayRef<PortInfo> firrtlPorts,
967 SmallVectorImpl<hw::PortInfo> &ports,
968 Operation *moduleOp, StringRef moduleName,
970 ports.reserve(firrtlPorts.size());
972 size_t numResults = 0;
973 for (
auto e :
llvm::enumerate(firrtlPorts)) {
975 size_t portNo = e.index();
980 if (firrtlPort.
sym.size() > 1 ||
981 (firrtlPort.
sym.size() == 1 && !firrtlPort.
sym.getSymName()))
982 return emitError(firrtlPort.
loc)
983 <<
"cannot lower aggregate port " << firrtlPort.
name
984 <<
" with field sensitive symbols, HW dialect does not support "
985 "per field symbols yet.";
986 hwPort.
setSym(firrtlPort.
sym, moduleOp->getContext());
988 if (hadDontTouch && !hwPort.
getSym()) {
989 if (hwPort.
type.isInteger(0)) {
990 if (enableAnnotationWarning) {
991 mlir::emitWarning(firrtlPort.
loc)
992 <<
"zero width port " << hwPort.
name
993 <<
" has dontTouch annotation, removing anyway";
999 hw::InnerSymAttr::get(StringAttr::get(
1000 moduleOp->getContext(),
1001 Twine(
"__") + moduleName + Twine(
"__DONTTOUCH__") +
1002 Twine(portNo) + Twine(
"__") + firrtlPort.
name.strref())),
1003 moduleOp->getContext());
1008 moduleOp->emitError(
"cannot lower this port type to HW");
1014 if (hwPort.
type.isInteger(0)) {
1015 auto sym = hwPort.
getSym();
1016 if (sym && !sym.empty()) {
1017 return mlir::emitError(firrtlPort.
loc)
1018 <<
"zero width port " << hwPort.
name
1019 <<
" is referenced by name [" << sym
1020 <<
"] (e.g. in an XMR) but must be removed";
1027 hwPort.
dir = hw::ModulePort::Direction::Output;
1028 hwPort.
argNum = numResults++;
1029 }
else if (firrtlPort.
isInput()) {
1030 hwPort.
dir = hw::ModulePort::Direction::Input;
1031 hwPort.
argNum = numArgs++;
1035 hwPort.
type = hw::InOutType::get(hwPort.
type);
1036 hwPort.
dir = hw::ModulePort::Direction::InOut;
1037 hwPort.
argNum = numArgs++;
1039 hwPort.
loc = firrtlPort.
loc;
1040 ports.push_back(hwPort);
1050 auto params = llvm::map_range(module.getParameters(), [](Attribute a) {
1051 return cast<ParamDeclAttr>(a);
1056 Builder builder(module);
1061 SmallVector<Attribute> newParams;
1062 for (
const ParamDeclAttr &entry : params) {
1063 auto name = entry.getName();
1064 auto type = entry.getType();
1065 auto value = ignoreValues ? Attribute() : entry.getValue();
1067 hw::ParamDeclAttr::get(builder.getContext(), name, type, value);
1068 newParams.push_back(paramAttr);
1070 return builder.getArrayAttr(newParams);
1073bool FIRRTLModuleLowering::handleForceNameAnnos(
1076 bool failed =
false;
1082 auto sym = anno.
getMember<FlatSymbolRefAttr>(
"circt.nonlocal");
1089 auto diag = oldModule.emitOpError()
1091 <<
"' that is not a non-local annotation";
1092 diag.attachNote() <<
"the erroneous annotation is '" << anno.
getDict()
1103 auto diag = oldModule.emitOpError()
1105 <<
"' whose non-local symbol, '" << sym
1106 <<
"' does not exist in the circuit";
1107 diag.attachNote() <<
"the erroneous annotation is '" << anno.
getDict();
1120 cast<hw::InnerRefAttr>(nla.getNamepath().getValue().take_back(2)[0]);
1122 {{inst.getModule(), inst.getName()}, anno.
getMember(
"name")});
1123 if (!inserted.second &&
1124 (anno.
getMember(
"name") != (inserted.first->second))) {
1125 auto diag = oldModule.emitError()
1127 <<
"' with different names: " << inserted.first->second
1128 <<
" was not " << anno.
getMember(
"name");
1129 diag.attachNote() <<
"the erroneous annotation is '" << anno.
getDict()
1140FIRRTLModuleLowering::lowerExtModule(FExtModuleOp oldModule,
1141 Block *topLevelModule,
1144 SmallVector<PortInfo> firrtlPorts = oldModule.getPorts();
1145 SmallVector<hw::PortInfo, 8> ports;
1146 if (failed(lowerPorts(firrtlPorts, ports, oldModule, oldModule.getName(),
1150 StringRef verilogName;
1151 if (
auto defName = oldModule.getDefname())
1152 verilogName = defName.value();
1155 auto builder = OpBuilder::atBlockEnd(topLevelModule);
1156 auto nameAttr = builder.getStringAttr(oldModule.getName());
1162 oldModule.getLoc(), nameAttr, ports, verilogName, parameters);
1163 SymbolTable::setSymbolVisibility(newModule,
1164 SymbolTable::getSymbolVisibility(oldModule));
1166 bool hasOutputPort =
1167 llvm::any_of(firrtlPorts, [&](
auto p) {
return p.isOutput(); });
1168 if (!hasOutputPort &&
1171 newModule->setAttr(
"firrtl.extract.cover.extra", builder.getUnitAttr());
1177 loweringState.processRemainingAnnotations(oldModule, annos);
1182FIRRTLModuleLowering::lowerMemModule(FMemModuleOp oldModule,
1183 Block *topLevelModule,
1186 SmallVector<PortInfo> firrtlPorts = oldModule.getPorts();
1187 SmallVector<hw::PortInfo, 8> ports;
1188 if (failed(lowerPorts(firrtlPorts, ports, oldModule, oldModule.getName(),
1193 auto builder = OpBuilder::atBlockEnd(topLevelModule);
1195 oldModule.getLoc(), oldModule.getModuleNameAttr(), ports,
1196 oldModule.getModuleNameAttr());
1204FIRRTLModuleLowering::lowerModule(FModuleOp oldModule, Block *topLevelModule,
1207 SmallVector<PortInfo> firrtlPorts = oldModule.getPorts();
1208 SmallVector<hw::PortInfo, 8> ports;
1209 if (failed(lowerPorts(firrtlPorts, ports, oldModule, oldModule.getName(),
1214 auto builder = OpBuilder::atBlockEnd(topLevelModule);
1215 auto nameAttr = builder.getStringAttr(oldModule.getName());
1217 builder.create<
hw::HWModuleOp>(oldModule.getLoc(), nameAttr, ports);
1219 if (
auto comment = oldModule->getAttrOfType<StringAttr>(
"comment"))
1220 newModule.setCommentAttr(comment);
1223 SmallVector<StringRef, 12> attrNames = {
1224 "annotations",
"convention",
"layers",
1225 "portNames",
"sym_name",
"portDirections",
1226 "portTypes",
"portAnnotations",
"portSymbols",
1227 "portLocations",
"parameters", SymbolTable::getVisibilityAttrName()};
1229 DenseSet<StringRef> attrSet(attrNames.begin(), attrNames.end());
1230 SmallVector<NamedAttribute> newAttrs(newModule->getAttrs());
1232 llvm::make_filter_range(oldModule->getAttrs(), [&](auto namedAttr) {
1233 return !attrSet.count(namedAttr.getName()) &&
1234 !newModule->getAttrDictionary().contains(namedAttr.getName());
1236 newAttrs.push_back(i);
1238 newModule->setAttrs(newAttrs);
1242 SymbolTable::setSymbolVisibility(newModule,
1243 SymbolTable::getSymbolVisibility(oldModule));
1249 newModule->setAttr(
"firrtl.extract.cover.extra", builder.getUnitAttr());
1253 if (
auto testBenchDir =
loweringState.getTestBenchDirectory())
1255 if (!newModule->hasAttr(
"output_file"))
1256 newModule->setAttr(
"output_file", testBenchDir);
1257 newModule->setAttr(
"firrtl.extract.do_not_extract",
1258 builder.getUnitAttr());
1259 newModule.setCommentAttr(
1260 builder.getStringAttr(
"VCS coverage exclude_file"));
1266 loweringState.processRemainingAnnotations(oldModule, annos);
1275 Operation *insertPoint) {
1276 if (!value.hasOneUse())
1279 auto attach = dyn_cast<AttachOp>(*value.user_begin());
1280 if (!attach || attach.getNumOperands() != 2)
1284 auto loweredType =
lowerType(value.getType());
1285 if (loweredType.isInteger(0))
1290 auto attachedValue = attach.getOperand(attach.getOperand(0) == value);
1291 auto *op = attachedValue.getDefiningOp();
1292 if (op && op->getBlock() == insertPoint->getBlock() &&
1293 !op->isBeforeInBlock(insertPoint))
1298 ImplicitLocOpBuilder builder(insertPoint->getLoc(), insertPoint);
1314 if (type_isa<AnalogType>(flipValue.getType()))
1317 Operation *connectOp =
nullptr;
1318 for (
auto &use : flipValue.getUses()) {
1321 if (use.getOperandNumber() != 0)
1323 if (!isa<ConnectOp, MatchingConnectOp>(use.getOwner()))
1329 connectOp = use.getOwner();
1339 loweringState.lowerType(flipValue.getType(), flipValue.getLoc());
1340 if (loweredType.isInteger(0))
1345 ImplicitLocOpBuilder builder(insertPoint->getLoc(), insertPoint);
1347 auto connectSrc = connectOp->getOperand(1);
1350 if (!isa<FIRRTLType>(connectSrc.getType())) {
1356 if (!type_cast<FIRRTLBaseType>(connectSrc.getType()).isPassive())
1357 connectSrc = builder
1358 .create<mlir::UnrealizedConversionCastOp>(
1359 type_cast<FIRRTLBaseType>(connectSrc.getType())
1366 auto destTy = type_cast<FIRRTLBaseType>(flipValue.getType()).getPassiveType();
1368 if (destTy != connectSrc.getType() &&
1369 (isa<BaseTypeAliasType>(connectSrc.getType()) ||
1370 isa<BaseTypeAliasType>(destTy))) {
1372 builder.createOrFold<BitCastOp>(flipValue.getType(), connectSrc);
1374 if (!destTy.isGround()) {
1376 if (destTy != type_cast<FIRRTLType>(connectSrc.getType()))
1378 }
else if (destTy.getBitWidthOrSentinel() !=
1379 type_cast<FIRRTLBaseType>(connectSrc.getType())
1380 .getBitWidthOrSentinel()) {
1383 auto destWidth = destTy.getBitWidthOrSentinel();
1384 assert(destWidth != -1 &&
"must know integer widths");
1385 connectSrc = builder.createOrFold<PadPrimOp>(destTy, connectSrc, destWidth);
1397 SmallVector<SubfieldOp> accesses;
1398 for (
auto *op : structValue.getUsers()) {
1399 assert(isa<SubfieldOp>(op));
1400 auto fieldAccess = cast<SubfieldOp>(op);
1402 fieldAccess.getInput().getType().base().getElementIndex(field);
1403 if (elemIndex && *elemIndex == fieldAccess.getFieldIndex())
1404 accesses.push_back(fieldAccess);
1412LogicalResult FIRRTLModuleLowering::lowerModulePortsAndMoveBody(
1415 ImplicitLocOpBuilder bodyBuilder(oldModule.getLoc(), newModule.getBody());
1422 bodyBuilder.setInsertionPoint(cursor);
1425 SmallVector<PortInfo> firrtlPorts = oldModule.getPorts();
1426 assert(oldModule.getBody().getNumArguments() == firrtlPorts.size() &&
1427 "port count mismatch");
1429 SmallVector<Value, 4> outputs;
1432 auto *outputOp = newModule.getBodyBlock()->getTerminator();
1433 ImplicitLocOpBuilder outputBuilder(oldModule.getLoc(), outputOp);
1435 unsigned nextHWInputArg = 0;
1436 int hwPortIndex = -1;
1437 for (
auto [firrtlPortIndex, port] :
llvm::enumerate(firrtlPorts)) {
1439 auto oldArg = oldModule.getBody().getArgument(firrtlPortIndex);
1442 type_isa<FIRRTLBaseType>(port.type) &&
1443 type_cast<FIRRTLBaseType>(port.type).getBitWidthOrSentinel() == 0;
1447 if (!port.isOutput() && !isZeroWidth) {
1450 Value newArg = newModule.getBody().getArgument(nextHWInputArg++);
1456 oldArg.replaceAllUsesWith(newArg);
1462 if (isZeroWidth && port.isInput()) {
1463 Value newArg = bodyBuilder
1464 .create<WireOp>(port.type,
"." + port.getName().str() +
1467 oldArg.replaceAllUsesWith(newArg);
1475 outputs.push_back(value);
1476 assert(oldArg.use_empty() &&
"should have removed all uses of oldArg");
1482 auto newArg = bodyBuilder.create<WireOp>(
1483 port.type,
"." + port.getName().str() +
".output");
1486 oldArg.replaceAllUsesWith(newArg.getResult());
1489 auto resultHWType =
loweringState.lowerType(port.type, port.loc);
1490 if (!resultHWType.isInteger(0)) {
1493 outputs.push_back(output);
1496 if (
auto sym = newModule.getPort(hwPortIndex).getSym()) {
1497 newArg.setInnerSymAttr(sym);
1498 newModule.setPortSymbolAttr(hwPortIndex, {});
1504 outputOp->setOperands(outputs);
1507 auto &oldBlockInstList = oldModule.getBodyBlock()->getOperations();
1508 auto &newBlockInstList = newModule.getBodyBlock()->getOperations();
1509 newBlockInstList.splice(Block::iterator(cursor), oldBlockInstList,
1510 oldBlockInstList.begin(), oldBlockInstList.end());
1521FIRRTLModuleLowering::lowerFormalBody(verif::FormalOp newOp,
1523 auto builder = OpBuilder::atBlockEnd(&newOp.getBody().front());
1528 auto oldOp = cast<FormalOp>(
loweringState.getOldModule(newOp));
1529 auto moduleName = oldOp.getModuleNameAttr().getAttr();
1530 auto oldModule = cast<FModuleOp>(
1531 loweringState.getInstanceGraph().lookup(moduleName)->getModule());
1532 auto newModule = cast<hw::HWModuleOp>(
loweringState.getNewModule(oldModule));
1535 SmallVector<Value> symbolicInputs;
1536 for (
auto arg : newModule.getBody().getArguments())
1537 symbolicInputs.push_back(
1538 builder.create<
verif::SymbolicValueOp>(arg.
getLoc(), arg.getType()));
1541 builder.create<hw::InstanceOp>(newOp.getLoc(), newModule,
1542 newModule.getNameAttr(), symbolicInputs);
1549FIRRTLModuleLowering::lowerSimulationBody(verif::SimulationOp newOp,
1551 auto builder = OpBuilder::atBlockEnd(newOp.getBody());
1554 auto oldOp = cast<SimulationOp>(
loweringState.getOldModule(newOp));
1555 auto moduleName = oldOp.getModuleNameAttr().getAttr();
1556 auto oldModule = cast<FModuleLike>(
1557 *
loweringState.getInstanceGraph().lookup(moduleName)->getModule());
1559 cast<hw::HWModuleLike>(
loweringState.getNewModule(oldModule));
1563 SmallVector<Value> inputs(newOp.getBody()->args_begin(),
1564 newOp.getBody()->args_end());
1565 auto instOp = builder.create<hw::InstanceOp>(newOp.getLoc(), newModule,
1566 newModule.getNameAttr(), inputs);
1567 builder.create<verif::YieldOp>(newOp.getLoc(), instOp.getResults());
1577struct FIRRTLLowering :
public FIRRTLVisitor<FIRRTLLowering, LogicalResult> {
1579 FIRRTLLowering(
hw::HWModuleOp module, CircuitLoweringState &circuitState)
1580 : theModule(module), circuitState(circuitState),
1581 builder(module.
getLoc(), module.getContext()), moduleNamespace(module),
1582 backedgeBuilder(builder, module.
getLoc()) {}
1584 LogicalResult
run();
1587 Value getOrCreateClockConstant(seq::ClockConst clock);
1588 Value getOrCreateIntConstant(
const APInt &value);
1589 Value getOrCreateIntConstant(
unsigned numBits, uint64_t val,
1590 bool isSigned =
false) {
1591 return getOrCreateIntConstant(APInt(numBits, val, isSigned));
1593 Attribute getOrCreateAggregateConstantAttribute(Attribute value, Type type);
1594 Value getOrCreateXConstant(
unsigned numBits);
1595 Value getOrCreateZConstant(Type type);
1596 Value getPossiblyInoutLoweredValue(Value value);
1597 Value getLoweredValue(Value value);
1598 Value getLoweredNonClockValue(Value value);
1599 Value getLoweredAndExtendedValue(Value value, Type destType);
1600 Value getLoweredAndExtOrTruncValue(Value value, Type destType);
1601 LogicalResult setLowering(Value orig, Value result);
1602 LogicalResult setPossiblyFoldedLowering(Value orig, Value result);
1603 template <
typename ResultOpType,
typename... CtorArgTypes>
1604 LogicalResult setLoweringTo(Operation *orig, CtorArgTypes... args);
1605 template <
typename ResultOpType,
typename... CtorArgTypes>
1606 LogicalResult setLoweringToLTL(Operation *orig, CtorArgTypes... args);
1607 Backedge createBackedge(Location loc, Type type);
1608 Backedge createBackedge(Value orig, Type type);
1609 bool updateIfBackedge(Value dest, Value src);
1612 bool requiresInnerSymbol(hw::InnerSymbolOpInterface op) {
1617 if (
auto forceable = dyn_cast<Forceable>(op.getOperation()))
1618 if (forceable.isForceable())
1626 hw::InnerSymAttr lowerInnerSymbol(hw::InnerSymbolOpInterface op) {
1627 auto attr = op.getInnerSymAttr();
1631 if (requiresInnerSymbol(op))
1633 op.getContext(), attr, 0,
1638 void runWithInsertionPointAtEndOfBlock(
const std::function<
void(
void)> &fn,
1642 Value getReadValue(Value v);
1644 Value getNonClockValue(Value v);
1646 void addToAlwaysBlock(sv::EventControl clockEdge, Value clock,
1647 sv::ResetType resetStyle, sv::EventControl resetEdge,
1648 Value reset,
const std::function<
void(
void)> &body = {},
1649 const std::function<void(
void)> &resetBody = {});
1650 void addToAlwaysBlock(Value clock,
1651 const std::function<
void(
void)> &body = {}) {
1652 addToAlwaysBlock(sv::EventControl::AtPosEdge, clock, sv::ResetType(),
1653 sv::EventControl(), Value(), body,
1654 std::function<
void(
void)>());
1657 LogicalResult emitGuards(Location loc, ArrayRef<Attribute> guards,
1658 std::function<
void(
void)>
emit);
1659 void addToIfDefBlock(StringRef cond, std::function<
void(
void)> thenCtor,
1660 std::function<
void(
void)> elseCtor = {});
1661 void addToInitialBlock(std::function<
void(
void)> body);
1662 void addIfProceduralBlock(Value cond, std::function<
void(
void)> thenCtor,
1663 std::function<
void(
void)> elseCtor = {});
1664 Value getExtOrTruncAggregateValue(Value array,
FIRRTLBaseType sourceType,
1666 bool allowTruncate);
1667 Value createArrayIndexing(Value array, Value index);
1668 Value createValueWithMuxAnnotation(Operation *op,
bool isMux2);
1670 using FIRRTLVisitor<FIRRTLLowering, LogicalResult>::visitExpr;
1671 using FIRRTLVisitor<FIRRTLLowering, LogicalResult>::visitDecl;
1672 using FIRRTLVisitor<FIRRTLLowering, LogicalResult>::visitStmt;
1675 enum UnloweredOpResult { AlreadyLowered, NowLowered, LoweringFailure };
1676 UnloweredOpResult handleUnloweredOp(Operation *op);
1677 LogicalResult visitExpr(ConstantOp op);
1678 LogicalResult visitExpr(SpecialConstantOp op);
1679 LogicalResult visitExpr(SubindexOp op);
1680 LogicalResult visitExpr(SubaccessOp op);
1681 LogicalResult visitExpr(SubfieldOp op);
1682 LogicalResult visitExpr(VectorCreateOp op);
1683 LogicalResult visitExpr(BundleCreateOp op);
1684 LogicalResult visitExpr(FEnumCreateOp op);
1685 LogicalResult visitExpr(AggregateConstantOp op);
1686 LogicalResult visitExpr(IsTagOp op);
1687 LogicalResult visitExpr(SubtagOp op);
1690 if (
auto castOp = dyn_cast<mlir::UnrealizedConversionCastOp>(op))
1691 return visitUnrealizedConversionCast(castOp);
1696 LogicalResult visitDecl(WireOp op);
1697 LogicalResult visitDecl(NodeOp op);
1698 LogicalResult visitDecl(RegOp op);
1699 LogicalResult visitDecl(RegResetOp op);
1700 LogicalResult visitDecl(MemOp op);
1701 LogicalResult visitDecl(InstanceOp oldInstance);
1702 LogicalResult visitDecl(VerbatimWireOp op);
1703 LogicalResult visitDecl(ContractOp op);
1706 LogicalResult lowerNoopCast(Operation *op);
1707 LogicalResult visitExpr(AsSIntPrimOp op);
1708 LogicalResult visitExpr(AsUIntPrimOp op);
1709 LogicalResult visitExpr(AsClockPrimOp op);
1710 LogicalResult visitExpr(AsAsyncResetPrimOp op) {
return lowerNoopCast(op); }
1712 LogicalResult visitExpr(HWStructCastOp op);
1713 LogicalResult visitExpr(BitCastOp op);
1715 visitUnrealizedConversionCast(mlir::UnrealizedConversionCastOp op);
1716 LogicalResult visitExpr(CvtPrimOp op);
1717 LogicalResult visitExpr(NotPrimOp op);
1718 LogicalResult visitExpr(NegPrimOp op);
1719 LogicalResult visitExpr(PadPrimOp op);
1720 LogicalResult visitExpr(XorRPrimOp op);
1721 LogicalResult visitExpr(AndRPrimOp op);
1722 LogicalResult visitExpr(OrRPrimOp op);
1725 template <
typename ResultUnsignedOpType,
1726 typename ResultSignedOpType = ResultUnsignedOpType>
1727 LogicalResult lowerBinOp(Operation *op);
1728 template <
typename ResultOpType>
1729 LogicalResult lowerBinOpToVariadic(Operation *op);
1731 template <
typename ResultOpType>
1732 LogicalResult lowerElementwiseLogicalOp(Operation *op);
1734 LogicalResult lowerCmpOp(Operation *op, ICmpPredicate signedOp,
1735 ICmpPredicate unsignedOp);
1736 template <
typename SignedOp,
typename Un
signedOp>
1737 LogicalResult lowerDivLikeOp(Operation *op);
1739 LogicalResult visitExpr(CatPrimOp op);
1741 LogicalResult visitExpr(AndPrimOp op) {
1742 return lowerBinOpToVariadic<comb::AndOp>(op);
1744 LogicalResult visitExpr(OrPrimOp op) {
1745 return lowerBinOpToVariadic<comb::OrOp>(op);
1747 LogicalResult visitExpr(XorPrimOp op) {
1748 return lowerBinOpToVariadic<comb::XorOp>(op);
1750 LogicalResult visitExpr(ElementwiseOrPrimOp op) {
1751 return lowerElementwiseLogicalOp<comb::OrOp>(op);
1753 LogicalResult visitExpr(ElementwiseAndPrimOp op) {
1754 return lowerElementwiseLogicalOp<comb::AndOp>(op);
1756 LogicalResult visitExpr(ElementwiseXorPrimOp op) {
1757 return lowerElementwiseLogicalOp<comb::XorOp>(op);
1759 LogicalResult visitExpr(AddPrimOp op) {
1760 return lowerBinOpToVariadic<comb::AddOp>(op);
1762 LogicalResult visitExpr(EQPrimOp op) {
1763 return lowerCmpOp(op, ICmpPredicate::eq, ICmpPredicate::eq);
1765 LogicalResult visitExpr(NEQPrimOp op) {
1766 return lowerCmpOp(op, ICmpPredicate::ne, ICmpPredicate::ne);
1768 LogicalResult visitExpr(LTPrimOp op) {
1769 return lowerCmpOp(op, ICmpPredicate::slt, ICmpPredicate::ult);
1771 LogicalResult visitExpr(LEQPrimOp op) {
1772 return lowerCmpOp(op, ICmpPredicate::sle, ICmpPredicate::ule);
1774 LogicalResult visitExpr(GTPrimOp op) {
1775 return lowerCmpOp(op, ICmpPredicate::sgt, ICmpPredicate::ugt);
1777 LogicalResult visitExpr(GEQPrimOp op) {
1778 return lowerCmpOp(op, ICmpPredicate::sge, ICmpPredicate::uge);
1781 LogicalResult visitExpr(SubPrimOp op) {
return lowerBinOp<comb::SubOp>(op); }
1782 LogicalResult visitExpr(MulPrimOp op) {
1783 return lowerBinOpToVariadic<comb::MulOp>(op);
1785 LogicalResult visitExpr(DivPrimOp op) {
1786 return lowerDivLikeOp<comb::DivSOp, comb::DivUOp>(op);
1788 LogicalResult visitExpr(RemPrimOp op) {
1789 return lowerDivLikeOp<comb::ModSOp, comb::ModUOp>(op);
1793 LogicalResult visitExpr(IsXIntrinsicOp op);
1794 LogicalResult visitExpr(PlusArgsTestIntrinsicOp op);
1795 LogicalResult visitExpr(PlusArgsValueIntrinsicOp op);
1796 LogicalResult visitStmt(FPGAProbeIntrinsicOp op);
1797 LogicalResult visitExpr(ClockInverterIntrinsicOp op);
1798 LogicalResult visitExpr(ClockDividerIntrinsicOp op);
1799 LogicalResult visitExpr(SizeOfIntrinsicOp op);
1800 LogicalResult visitExpr(ClockGateIntrinsicOp op);
1801 LogicalResult visitExpr(LTLAndIntrinsicOp op);
1802 LogicalResult visitExpr(LTLOrIntrinsicOp op);
1803 LogicalResult visitExpr(LTLIntersectIntrinsicOp op);
1804 LogicalResult visitExpr(LTLDelayIntrinsicOp op);
1805 LogicalResult visitExpr(LTLConcatIntrinsicOp op);
1806 LogicalResult visitExpr(LTLRepeatIntrinsicOp op);
1807 LogicalResult visitExpr(LTLGoToRepeatIntrinsicOp op);
1808 LogicalResult visitExpr(LTLNonConsecutiveRepeatIntrinsicOp op);
1809 LogicalResult visitExpr(LTLNotIntrinsicOp op);
1810 LogicalResult visitExpr(LTLImplicationIntrinsicOp op);
1811 LogicalResult visitExpr(LTLUntilIntrinsicOp op);
1812 LogicalResult visitExpr(LTLEventuallyIntrinsicOp op);
1813 LogicalResult visitExpr(LTLClockIntrinsicOp op);
1815 template <
typename TargetOp,
typename IntrinsicOp>
1816 LogicalResult lowerVerifIntrinsicOp(IntrinsicOp op);
1817 LogicalResult visitStmt(VerifAssertIntrinsicOp op);
1818 LogicalResult visitStmt(VerifAssumeIntrinsicOp op);
1819 LogicalResult visitStmt(VerifCoverIntrinsicOp op);
1820 LogicalResult visitStmt(VerifRequireIntrinsicOp op);
1821 LogicalResult visitStmt(VerifEnsureIntrinsicOp op);
1822 LogicalResult visitExpr(HasBeenResetIntrinsicOp op);
1823 LogicalResult visitStmt(UnclockedAssumeIntrinsicOp op);
1826 LogicalResult visitExpr(BitsPrimOp op);
1827 LogicalResult visitExpr(InvalidValueOp op);
1828 LogicalResult visitExpr(HeadPrimOp op);
1829 LogicalResult visitExpr(ShlPrimOp op);
1830 LogicalResult visitExpr(ShrPrimOp op);
1831 LogicalResult visitExpr(DShlPrimOp op) {
1832 return lowerDivLikeOp<comb::ShlOp, comb::ShlOp>(op);
1834 LogicalResult visitExpr(DShrPrimOp op) {
1835 return lowerDivLikeOp<comb::ShrSOp, comb::ShrUOp>(op);
1837 LogicalResult visitExpr(DShlwPrimOp op) {
1838 return lowerDivLikeOp<comb::ShlOp, comb::ShlOp>(op);
1840 LogicalResult visitExpr(TailPrimOp op);
1841 LogicalResult visitExpr(MuxPrimOp op);
1842 LogicalResult visitExpr(Mux2CellIntrinsicOp op);
1843 LogicalResult visitExpr(Mux4CellIntrinsicOp op);
1844 LogicalResult visitExpr(MultibitMuxOp op);
1845 LogicalResult visitExpr(VerbatimExprOp op);
1846 LogicalResult visitExpr(XMRRefOp op);
1847 LogicalResult visitExpr(XMRDerefOp op);
1850 LogicalResult visitExpr(TimeOp op);
1851 LogicalResult visitExpr(HierarchicalModuleNameOp op);
1854 LogicalResult lowerVerificationStatement(
1855 Operation *op, StringRef labelPrefix, Value clock, Value predicate,
1856 Value enable, StringAttr messageAttr, ValueRange operands,
1857 StringAttr nameAttr,
bool isConcurrent, EventControl eventControl);
1859 LogicalResult visitStmt(SkipOp op);
1861 FailureOr<bool> lowerConnect(Value dest, Value srcVal);
1862 LogicalResult visitStmt(ConnectOp op);
1863 LogicalResult visitStmt(MatchingConnectOp op);
1864 LogicalResult visitStmt(ForceOp op);
1866 std::optional<Value> getLoweredFmtOperand(Value operand);
1867 LogicalResult loweredFmtOperands(ValueRange operands,
1868 SmallVectorImpl<Value> &loweredOperands);
1869 FailureOr<Value> callFileDescriptorLib(
const FileDescriptorInfo &info);
1873 LogicalResult lowerStatementWithFd(
1874 const FileDescriptorInfo &fileDescriptorInfo, Value clock, Value cond,
1875 const std::function<LogicalResult(Value)> &fn,
bool usePrintfCond);
1879 LogicalResult visitPrintfLike(T op,
1880 const FileDescriptorInfo &fileDescriptorInfo,
1881 bool usePrintfCond);
1882 LogicalResult visitStmt(PrintFOp op) {
return visitPrintfLike(op, {},
true); }
1883 LogicalResult visitStmt(FPrintFOp op);
1884 LogicalResult visitStmt(FFlushOp op);
1885 LogicalResult visitStmt(StopOp op);
1886 LogicalResult visitStmt(AssertOp op);
1887 LogicalResult visitStmt(AssumeOp op);
1888 LogicalResult visitStmt(CoverOp op);
1889 LogicalResult visitStmt(AttachOp op);
1890 LogicalResult visitStmt(RefForceOp op);
1891 LogicalResult visitStmt(RefForceInitialOp op);
1892 LogicalResult visitStmt(RefReleaseOp op);
1893 LogicalResult visitStmt(RefReleaseInitialOp op);
1894 LogicalResult visitStmt(BindOp op);
1896 FailureOr<Value> lowerSubindex(SubindexOp op, Value input);
1897 FailureOr<Value> lowerSubaccess(SubaccessOp op, Value input);
1898 FailureOr<Value> lowerSubfield(SubfieldOp op, Value input);
1900 LogicalResult fixupLTLOps();
1903 return circuitState.lowerType(type, builder.getLoc());
1911 CircuitLoweringState &circuitState;
1914 ImplicitLocOpBuilder builder;
1919 DenseMap<Value, Value> valueMapping;
1923 DenseMap<Value, Value> fromClockMapping;
1927 DenseMap<Attribute, Value> hwConstantMap;
1928 DenseMap<std::pair<Attribute, Type>, Attribute> hwAggregateConstantMap;
1932 DenseMap<unsigned, Value> hwConstantXMap;
1933 DenseMap<Type, Value> hwConstantZMap;
1939 DenseMap<Value, Value> readInOutCreated;
1942 DenseMap<StringAttr, sv::RegOp> fileNameToFileDescriptor;
1946 using AlwaysKeyType = std::tuple<
Block *, sv::EventControl, Value,
1947 sv::ResetType, sv::EventControl, Value>;
1964 llvm::MapVector<Value, Value> backedges;
1971 DenseSet<Operation *> maybeUnusedValues;
1973 void maybeUnused(Operation *op) { maybeUnusedValues.insert(op); }
1974 void maybeUnused(Value value) {
1975 if (
auto *op = value.getDefiningOp())
1987 SetVector<Operation *> ltlOpFixupWorklist;
1992 SmallVector<std::pair<Block::iterator, Block::iterator>> worklist;
1994 void addToWorklist(Block &block) {
1995 worklist.push_back({block.begin(), block.end()});
1997 void addToWorklist(Region ®ion) {
1998 for (
auto &block :
llvm::reverse(region))
1999 addToWorklist(block);
2010LogicalResult FIRRTLModuleLowering::lowerFileBody(emit::FileOp fileOp) {
2011 OpBuilder b(&getContext());
2012 fileOp->walk([&](Operation *op) {
2013 if (
auto bindOp = dyn_cast<BindOp>(op)) {
2014 b.setInsertionPointAfter(bindOp);
2015 b.create<sv::BindOp>(bindOp.getLoc(), bindOp.getInstanceAttr());
2023FIRRTLModuleLowering::lowerBody(Operation *op,
2025 if (
auto moduleOp = dyn_cast<hw::HWModuleOp>(op))
2027 if (
auto formalOp = dyn_cast<verif::FormalOp>(op))
2029 if (
auto simulationOp = dyn_cast<verif::SimulationOp>(op))
2031 if (
auto fileOp = dyn_cast<emit::FileOp>(op))
2032 return lowerFileBody(fileOp);
2037LogicalResult FIRRTLLowering::run() {
2040 for (
auto arg : theModule.
getBodyBlock()->getArguments())
2041 if (failed(setLowering(arg, arg)))
2048 addToWorklist(theModule.getBody());
2049 SmallVector<Operation *, 16> opsToRemove;
2051 while (!worklist.empty()) {
2052 auto &[opsIt, opsEnd] = worklist.back();
2053 if (opsIt == opsEnd) {
2054 worklist.pop_back();
2057 Operation *op = &*opsIt++;
2059 builder.setInsertionPoint(op);
2060 builder.setLoc(op->getLoc());
2061 auto done = succeeded(dispatchVisitor(op));
2062 circuitState.processRemainingAnnotations(op,
AnnotationSet(op));
2064 opsToRemove.push_back(op);
2066 switch (handleUnloweredOp(op)) {
2067 case AlreadyLowered:
2070 opsToRemove.push_back(op);
2072 case LoweringFailure:
2084 for (
auto &[backedge, value] : backedges) {
2085 SmallVector<Location> driverLocs;
2091 if (backedge == value) {
2092 Location edgeLoc = backedge.getLoc();
2093 if (driverLocs.empty()) {
2094 mlir::emitError(edgeLoc,
"sink does not have a driver");
2096 auto diag = mlir::emitError(edgeLoc,
"sink in combinational loop");
2097 for (
auto loc : driverLocs)
2098 diag.attachNote(loc) <<
"through driver here";
2104 auto *it = backedges.find(value);
2105 if (it == backedges.end())
2108 driverLocs.push_back(value.getLoc());
2111 if (
auto *defOp = backedge.getDefiningOp())
2112 maybeUnusedValues.erase(defOp);
2113 backedge.replaceAllUsesWith(value);
2121 while (!opsToRemove.empty()) {
2122 auto *op = opsToRemove.pop_back_val();
2127 for (
auto result : op->getResults()) {
2131 auto builder = OpBuilder::atBlockBegin(theModule.getBodyBlock());
2133 builder.getIntegerType(0), 0);
2134 maybeUnusedValues.insert(zeroI0);
2136 result.replaceAllUsesWith(zeroI0);
2139 if (!op->use_empty()) {
2140 auto d = op->emitOpError(
2141 "still has uses; should remove ops in reverse order of visitation");
2142 SmallPtrSet<Operation *, 2> visited;
2143 for (
auto *user : op->getUsers())
2144 if (visited.insert(user).second)
2145 d.attachNote(user->
getLoc())
2146 <<
"used by " << user->
getName() <<
" op";
2149 maybeUnusedValues.erase(op);
2154 while (!maybeUnusedValues.empty()) {
2155 auto it = maybeUnusedValues.begin();
2157 maybeUnusedValues.erase(it);
2158 if (!isOpTriviallyDead(op))
2160 for (
auto operand : op->getOperands())
2161 if (auto *defOp = operand.getDefiningOp())
2162 maybeUnusedValues.insert(defOp);
2168 if (failed(fixupLTLOps()))
2179Value FIRRTLLowering::getOrCreateClockConstant(seq::ClockConst clock) {
2180 auto attr = seq::ClockConstAttr::get(theModule.getContext(), clock);
2182 auto &entry = hwConstantMap[attr];
2186 OpBuilder entryBuilder(&theModule.getBodyBlock()->front());
2187 entry = entryBuilder.create<seq::ConstClockOp>(builder.getLoc(), attr);
2193Value FIRRTLLowering::getOrCreateIntConstant(
const APInt &value) {
2194 auto attr = builder.getIntegerAttr(
2195 builder.getIntegerType(value.getBitWidth()), value);
2197 auto &entry = hwConstantMap[attr];
2201 OpBuilder entryBuilder(&theModule.getBodyBlock()->front());
2202 entry = entryBuilder.create<
hw::ConstantOp>(builder.getLoc(), attr);
2208Attribute FIRRTLLowering::getOrCreateAggregateConstantAttribute(Attribute value,
2211 if (hw::type_isa<IntegerType>(type))
2212 return builder.getIntegerAttr(type, cast<IntegerAttr>(value).getValue());
2214 auto cache = hwAggregateConstantMap.lookup({value, type});
2219 SmallVector<Attribute> values;
2220 for (
auto e :
llvm::enumerate(cast<ArrayAttr>(value))) {
2222 if (
auto array = hw::type_dyn_cast<hw::ArrayType>(type))
2223 subType = array.getElementType();
2224 else if (
auto structType = hw::type_dyn_cast<hw::StructType>(type))
2225 subType = structType.getElements()[e.index()].type;
2227 assert(
false &&
"type must be either array or struct");
2229 values.push_back(getOrCreateAggregateConstantAttribute(e.value(), subType));
2233 if (hw::type_isa<hw::ArrayType>(type))
2234 std::reverse(values.begin(), values.end());
2236 auto &entry = hwAggregateConstantMap[{value, type}];
2237 entry = builder.getArrayAttr(values);
2245 const std::function<LogicalResult()> &fn) {
2246 assert(failedOperand &&
"Should be called on the failed operand");
2254Value FIRRTLLowering::getOrCreateXConstant(
unsigned numBits) {
2256 auto &entry = hwConstantXMap[numBits];
2260 OpBuilder entryBuilder(&theModule.getBodyBlock()->front());
2261 entry = entryBuilder.create<sv::ConstantXOp>(
2262 builder.getLoc(), entryBuilder.getIntegerType(numBits));
2266Value FIRRTLLowering::getOrCreateZConstant(Type type) {
2267 auto &entry = hwConstantZMap[type];
2269 OpBuilder entryBuilder(&theModule.getBodyBlock()->front());
2270 entry = entryBuilder.create<sv::ConstantZOp>(builder.getLoc(), type);
2279Value FIRRTLLowering::getPossiblyInoutLoweredValue(Value value) {
2281 if (
auto lowering = valueMapping.lookup(value)) {
2282 assert(!isa<FIRRTLType>(lowering.getType()) &&
2283 "Lowered value should be a non-FIRRTL value");
2292Value FIRRTLLowering::getLoweredValue(Value value) {
2293 auto result = getPossiblyInoutLoweredValue(value);
2299 if (isa<hw::InOutType>(result.getType()))
2300 return getReadValue(result);
2306Value FIRRTLLowering::getLoweredNonClockValue(Value value) {
2307 auto result = getLoweredValue(value);
2311 if (hw::type_isa<seq::ClockType>(result.getType()))
2312 return getNonClockValue(result);
2320Value FIRRTLLowering::getExtOrTruncAggregateValue(Value array,
2323 bool allowTruncate) {
2324 SmallVector<Value> resultBuffer;
2329 auto srcWidth = firrtl::type_cast<IntType>(sourceType).getWidthOrSentinel();
2330 auto destWidth = firrtl::type_cast<IntType>(destType).getWidthOrSentinel();
2331 auto resultType = builder.getIntegerType(destWidth);
2333 if (srcWidth == destWidth)
2336 if (srcWidth > destWidth) {
2340 builder.emitError(
"operand should not be a truncation");
2344 if (firrtl::type_cast<IntType>(sourceType).isSigned())
2345 return comb::createOrFoldSExt(value, resultType, builder);
2346 auto zero = getOrCreateIntConstant(destWidth - srcWidth, 0);
2354 return TypeSwitch<FIRRTLBaseType, LogicalResult>(srcType)
2355 .Case<FVectorType>([&](
auto srcVectorType) {
2356 auto destVectorType = firrtl::type_cast<FVectorType>(destType);
2357 unsigned size = resultBuffer.size();
2358 unsigned indexWidth =
2360 for (
size_t i = 0, e = std::min(srcVectorType.getNumElements(),
2361 destVectorType.getNumElements());
2363 auto iIdx = getOrCreateIntConstant(indexWidth, i);
2365 if (failed(recurse(arrayIndex, srcVectorType.getElementType(),
2366 destVectorType.getElementType())))
2369 SmallVector<Value> temp(resultBuffer.begin() + size,
2370 resultBuffer.end());
2372 resultBuffer.resize(size);
2373 resultBuffer.push_back(array);
2376 .Case<BundleType>([&](BundleType srcStructType) {
2377 auto destStructType = firrtl::type_cast<BundleType>(destType);
2378 unsigned size = resultBuffer.size();
2381 if (destStructType.getNumElements() != srcStructType.getNumElements())
2384 for (
auto elem :
llvm::enumerate(destStructType)) {
2385 auto structExtract =
2387 if (failed(recurse(structExtract,
2388 srcStructType.getElementType(elem.index()),
2389 destStructType.getElementType(elem.index()))))
2392 SmallVector<Value> temp(resultBuffer.begin() + size,
2393 resultBuffer.end());
2396 resultBuffer.resize(size);
2397 resultBuffer.push_back(newStruct);
2400 .Case<IntType>([&](
auto) {
2401 if (
auto result = cast(src, srcType, destType)) {
2402 resultBuffer.push_back(result);
2407 .Default([&](
auto) {
return failure(); });
2410 if (failed(recurse(array, sourceType, destType)))
2413 assert(resultBuffer.size() == 1 &&
2414 "resultBuffer must only contain a result array if `success` is true");
2415 return resultBuffer[0];
2423Value FIRRTLLowering::getLoweredAndExtendedValue(Value value, Type destType) {
2424 assert(type_isa<FIRRTLBaseType>(value.getType()) &&
2425 type_isa<FIRRTLBaseType>(destType) &&
2426 "input/output value should be FIRRTL");
2429 auto destWidth = type_cast<FIRRTLBaseType>(destType).getBitWidthOrSentinel();
2430 if (destWidth == -1)
2433 auto result = getLoweredValue(value);
2445 return getOrCreateIntConstant(destWidth, 0);
2449 cast<FIRRTLBaseType>(value.getType()).getBitWidthOrSentinel()) {
2451 auto loweredDstType =
lowerType(destType);
2452 if (result.getType() != loweredDstType &&
2453 (isa<hw::TypeAliasType>(result.getType()) ||
2454 isa<hw::TypeAliasType>(loweredDstType))) {
2455 return builder.createOrFold<
hw::BitcastOp>(loweredDstType, result);
2459 if (isa<hw::ArrayType, hw::StructType>(result.getType())) {
2461 if (destType == value.getType())
2464 return getExtOrTruncAggregateValue(
2465 result, type_cast<FIRRTLBaseType>(value.getType()),
2466 type_cast<FIRRTLBaseType>(destType),
2470 if (isa<seq::ClockType>(result.getType())) {
2472 if (destType == value.getType())
2474 builder.emitError(
"cannot use clock type as an integer");
2478 auto intResultType = dyn_cast<IntegerType>(result.getType());
2479 if (!intResultType) {
2480 builder.emitError(
"operand of type ")
2481 << result.getType() <<
" cannot be used as an integer";
2485 auto srcWidth = intResultType.getWidth();
2486 if (srcWidth ==
unsigned(destWidth))
2489 if (srcWidth >
unsigned(destWidth)) {
2490 builder.emitError(
"operand should not be a truncation");
2494 auto resultType = builder.getIntegerType(destWidth);
2498 type_cast<FIRRTLBaseType>(value.getType()).getPassiveType();
2499 if (type_cast<IntType>(valueFIRType).isSigned())
2500 return comb::createOrFoldSExt(result, resultType, builder);
2502 auto zero = getOrCreateIntConstant(destWidth - srcWidth, 0);
2511Value FIRRTLLowering::getLoweredAndExtOrTruncValue(Value value, Type destType) {
2512 assert(type_isa<FIRRTLBaseType>(value.getType()) &&
2513 type_isa<FIRRTLBaseType>(destType) &&
2514 "input/output value should be FIRRTL");
2517 auto destWidth = type_cast<FIRRTLBaseType>(destType).getBitWidthOrSentinel();
2518 if (destWidth == -1)
2521 auto result = getLoweredValue(value);
2533 return getOrCreateIntConstant(destWidth, 0);
2537 if (isa<hw::ArrayType, hw::StructType>(result.getType())) {
2539 if (destType == value.getType())
2542 return getExtOrTruncAggregateValue(
2543 result, type_cast<FIRRTLBaseType>(value.getType()),
2544 type_cast<FIRRTLBaseType>(destType),
2548 auto srcWidth = type_cast<IntegerType>(result.getType()).getWidth();
2549 if (srcWidth ==
unsigned(destWidth))
2555 if (srcWidth >
unsigned(destWidth)) {
2556 auto resultType = builder.getIntegerType(destWidth);
2560 auto resultType = builder.getIntegerType(destWidth);
2564 type_cast<FIRRTLBaseType>(value.getType()).getPassiveType();
2565 if (type_cast<IntType>(valueFIRType).isSigned())
2566 return comb::createOrFoldSExt(result, resultType, builder);
2568 auto zero = getOrCreateIntConstant(destWidth - srcWidth, 0);
2582std::optional<Value> FIRRTLLowering::getLoweredFmtOperand(Value operand) {
2584 if (type_isa<FStringType>(operand.getType())) {
2585 if (isa<TimeOp>(operand.getDefiningOp()))
2586 return builder.create<sv::TimeOp>();
2587 if (isa<HierarchicalModuleNameOp>(operand.getDefiningOp()))
2591 auto loweredValue = getLoweredValue(operand);
2592 if (!loweredValue) {
2596 loweredValue = getOrCreateIntConstant(1, 0);
2601 if (
auto intTy = firrtl::type_cast<IntType>(operand.getType()))
2602 if (intTy.isSigned())
2603 loweredValue = builder.create<sv::SystemFunctionOp>(
2604 loweredValue.getType(),
"signed", loweredValue);
2606 return loweredValue;
2610FIRRTLLowering::loweredFmtOperands(mlir::ValueRange operands,
2611 SmallVectorImpl<Value> &loweredOperands) {
2612 for (
auto operand : operands) {
2613 std::optional<Value> loweredValue = getLoweredFmtOperand(operand);
2618 loweredOperands.push_back(*loweredValue);
2623LogicalResult FIRRTLLowering::lowerStatementWithFd(
2624 const FileDescriptorInfo &fileDescriptor, Value clock, Value cond,
2625 const std::function<LogicalResult(Value)> &fn,
bool usePrintfCond) {
2627 bool failed =
false;
2628 circuitState.addMacroDecl(builder.getStringAttr(
"SYNTHESIS"));
2629 addToIfDefBlock(
"SYNTHESIS", std::function<
void()>(), [&]() {
2630 addToAlwaysBlock(clock, [&]() {
2633 circuitState.usedPrintf =
true;
2635 circuitState.addFragment(theModule,
"PRINTF_COND_FRAGMENT");
2638 Value ifCond = cond;
2639 if (usePrintfCond) {
2641 builder.create<sv::MacroRefExprOp>(cond.getType(),
"PRINTF_COND_");
2642 ifCond = builder.createOrFold<
comb::AndOp>(ifCond, cond,
true);
2645 addIfProceduralBlock(ifCond, [&]() {
2649 if (fileDescriptor.isDefaultFd()) {
2651 fd = builder.create<hw::ConstantOp>(APInt(32, 0x80000002));
2654 auto fdOrError = callFileDescriptorLib(fileDescriptor);
2655 if (llvm::failed(fdOrError)) {
2661 failed = llvm::failed(fn(fd));
2665 return failure(failed);
2669FIRRTLLowering::callFileDescriptorLib(
const FileDescriptorInfo &info) {
2670 circuitState.usedFileDescriptorLib =
true;
2671 circuitState.addFragment(theModule,
"CIRCT_LIB_LOGGING_FRAGMENT");
2674 if (
info.isSubstitutionRequired()) {
2675 SmallVector<Value> fileNameOperands;
2676 if (failed(loweredFmtOperands(
info.getSubstitutions(), fileNameOperands)))
2680 .create<sv::SFormatFOp>(
info.getOutputFileFormat(),
2685 fileName = builder.create<sv::ConstantStrOp>(
info.getOutputFileFormat())
2690 .create<sv::FuncCallProceduralOp>(
2691 mlir::TypeRange{builder.getIntegerType(32)},
2692 builder.getStringAttr(
"__circt_lib_logging::FileDescriptor::get"),
2693 ValueRange{fileName})
2703LogicalResult FIRRTLLowering::setLowering(Value orig, Value result) {
2704 if (
auto origType = dyn_cast<FIRRTLType>(orig.getType())) {
2705 assert((!result || !type_isa<FIRRTLType>(result.getType())) &&
2706 "Lowering didn't turn a FIRRTL value into a non-FIRRTL value");
2710 auto srcWidth = baseType.getPassiveType().getBitWidthOrSentinel();
2713 if (srcWidth != -1) {
2715 assert((srcWidth != 0) &&
2716 "Lowering produced value for zero width source");
2718 assert((srcWidth == 0) &&
2719 "Lowering produced null value but source wasn't zero width");
2723 assert(result &&
"Lowering of foreign type produced null value");
2726 auto &slot = valueMapping[orig];
2727 assert(!slot &&
"value lowered multiple times");
2734LogicalResult FIRRTLLowering::setPossiblyFoldedLowering(Value orig,
2738 if (
auto cst = dyn_cast_or_null<hw::ConstantOp>(result.getDefiningOp())) {
2739 auto &entry = hwConstantMap[cst.getValueAttr()];
2750 cst->moveBefore(&theModule.getBodyBlock()->front());
2754 return setLowering(orig, result);
2759template <
typename ResultOpType,
typename... CtorArgTypes>
2760LogicalResult FIRRTLLowering::setLoweringTo(Operation *orig,
2761 CtorArgTypes... args) {
2762 auto result = builder.createOrFold<ResultOpType>(args...);
2763 if (
auto *op = result.getDefiningOp())
2765 return setPossiblyFoldedLowering(orig->getResult(0), result);
2772template <
typename ResultOpType,
typename... CtorArgTypes>
2773LogicalResult FIRRTLLowering::setLoweringToLTL(Operation *orig,
2774 CtorArgTypes... args) {
2775 auto result = builder.createOrFold<ResultOpType>(args...);
2776 if (
auto *op = result.getDefiningOp())
2777 ltlOpFixupWorklist.insert(op);
2778 return setPossiblyFoldedLowering(orig->getResult(0), result);
2787Backedge FIRRTLLowering::createBackedge(Location loc, Type type) {
2788 auto backedge = backedgeBuilder.
get(type, loc);
2789 backedges.insert({backedge, backedge});
2797Backedge FIRRTLLowering::createBackedge(Value orig, Type type) {
2798 auto backedge = createBackedge(orig.getLoc(), type);
2799 (void)setLowering(orig, backedge);
2805bool FIRRTLLowering::updateIfBackedge(Value dest, Value src) {
2806 auto backedgeIt = backedges.find(dest);
2807 if (backedgeIt == backedges.end())
2809 backedgeIt->second = src;
2817void FIRRTLLowering::runWithInsertionPointAtEndOfBlock(
2818 const std::function<
void(
void)> &fn, Region ®ion) {
2822 auto oldIP = builder.saveInsertionPoint();
2824 builder.setInsertionPointToEnd(®ion.front());
2826 builder.restoreInsertionPoint(oldIP);
2830Value FIRRTLLowering::getReadValue(Value v) {
2831 Value result = readInOutCreated.lookup(v);
2837 auto oldIP = builder.saveInsertionPoint();
2838 if (
auto *vOp = v.getDefiningOp()) {
2839 builder.setInsertionPointAfter(vOp);
2843 builder.setInsertionPoint(&theModule.getBodyBlock()->front());
2848 if (
auto arrayIndexInout = v.getDefiningOp<sv::ArrayIndexInOutOp>()) {
2849 result = getReadValue(arrayIndexInout.getInput());
2851 arrayIndexInout.getIndex());
2856 builder.restoreInsertionPoint(oldIP);
2857 readInOutCreated.insert({v, result});
2861Value FIRRTLLowering::getNonClockValue(Value v) {
2862 auto it = fromClockMapping.try_emplace(v, Value{});
2864 ImplicitLocOpBuilder builder(v.getLoc(), v.getContext());
2865 builder.setInsertionPointAfterValue(v);
2866 it.first->second = builder.create<seq::FromClockOp>(v);
2868 return it.first->second;
2871void FIRRTLLowering::addToAlwaysBlock(
2872 sv::EventControl clockEdge, Value clock, sv::ResetType resetStyle,
2873 sv::EventControl resetEdge, Value reset,
2874 const std::function<
void(
void)> &body,
2875 const std::function<
void(
void)> &resetBody) {
2876 AlwaysKeyType key{builder.getBlock(), clockEdge, clock,
2877 resetStyle, resetEdge, reset};
2878 sv::AlwaysOp alwaysOp;
2879 sv::IfOp insideIfOp;
2880 std::tie(alwaysOp, insideIfOp) = alwaysBlocks.lookup(key);
2884 assert(resetStyle != sv::ResetType::NoReset);
2897 auto createIfOp = [&]() {
2900 insideIfOp = builder.create<sv::IfOp>(
2901 reset, []() {}, []() {});
2903 if (resetStyle == sv::ResetType::AsyncReset) {
2904 sv::EventControl events[] = {clockEdge, resetEdge};
2905 Value clocks[] = {clock, reset};
2907 alwaysOp = builder.create<sv::AlwaysOp>(events, clocks, [&]() {
2908 if (resetEdge == sv::EventControl::AtNegEdge)
2909 llvm_unreachable(
"negative edge for reset is not expected");
2913 alwaysOp = builder.create<sv::AlwaysOp>(clockEdge, clock, createIfOp);
2917 alwaysOp = builder.create<sv::AlwaysOp>(clockEdge, clock);
2918 insideIfOp =
nullptr;
2920 alwaysBlocks[key] = {alwaysOp, insideIfOp};
2924 assert(insideIfOp &&
"reset body must be initialized before");
2925 runWithInsertionPointAtEndOfBlock(resetBody, insideIfOp.getThenRegion());
2926 runWithInsertionPointAtEndOfBlock(body, insideIfOp.getElseRegion());
2928 runWithInsertionPointAtEndOfBlock(body, alwaysOp.getBody());
2934 alwaysOp->moveBefore(builder.getInsertionBlock(),
2935 builder.getInsertionPoint());
2938LogicalResult FIRRTLLowering::emitGuards(Location loc,
2939 ArrayRef<Attribute> guards,
2940 std::function<
void(
void)>
emit) {
2941 if (guards.empty()) {
2945 auto guard = dyn_cast<StringAttr>(guards[0]);
2947 return mlir::emitError(loc,
2948 "elements in `guards` array must be `StringAttr`");
2951 circuitState.addMacroDecl(builder.getStringAttr(guard.getValue()));
2952 LogicalResult result = LogicalResult::failure();
2953 addToIfDefBlock(guard.getValue(), [&]() {
2954 result = emitGuards(loc, guards.drop_front(), emit);
2959void FIRRTLLowering::addToIfDefBlock(StringRef cond,
2960 std::function<
void(
void)> thenCtor,
2961 std::function<
void(
void)> elseCtor) {
2962 auto condAttr = builder.getStringAttr(cond);
2963 auto op = ifdefBlocks.lookup({builder.getBlock(), condAttr});
2965 runWithInsertionPointAtEndOfBlock(thenCtor, op.getThenRegion());
2966 runWithInsertionPointAtEndOfBlock(elseCtor, op.getElseRegion());
2971 op->moveBefore(builder.getInsertionBlock(), builder.getInsertionPoint());
2973 ifdefBlocks[{builder.getBlock(), condAttr}] =
2974 builder.create<
sv::IfDefOp>(condAttr, thenCtor, elseCtor);
2978void FIRRTLLowering::addToInitialBlock(std::function<
void(
void)> body) {
2979 auto op = initialBlocks.lookup(builder.getBlock());
2981 runWithInsertionPointAtEndOfBlock(body, op.getBody());
2986 op->moveBefore(builder.getInsertionBlock(), builder.getInsertionPoint());
2988 initialBlocks[builder.getBlock()] = builder.create<sv::InitialOp>(body);
2992void FIRRTLLowering::addIfProceduralBlock(Value cond,
2993 std::function<
void(
void)> thenCtor,
2994 std::function<
void(
void)> elseCtor) {
2997 auto insertIt = builder.getInsertionPoint();
2998 if (insertIt != builder.getBlock()->begin())
2999 if (
auto ifOp = dyn_cast<sv::IfOp>(*--insertIt)) {
3000 if (ifOp.getCond() == cond) {
3001 runWithInsertionPointAtEndOfBlock(thenCtor, ifOp.getThenRegion());
3002 runWithInsertionPointAtEndOfBlock(elseCtor, ifOp.getElseRegion());
3007 builder.create<sv::IfOp>(cond, thenCtor, elseCtor);
3019FIRRTLLowering::UnloweredOpResult
3020FIRRTLLowering::handleUnloweredOp(Operation *op) {
3022 if (!op->getRegions().empty() && isa<FIRRTLDialect>(op->getDialect())) {
3023 op->emitOpError(
"must explicitly handle its regions");
3024 return LoweringFailure;
3031 if (!isa<FIRRTLDialect>(op->getDialect())) {
3033 for (
auto ®ion : op->getRegions())
3034 addToWorklist(region);
3035 for (
auto &operand : op->getOpOperands())
3036 if (auto lowered = getPossiblyInoutLoweredValue(operand.
get()))
3037 operand.set(lowered);
3038 for (
auto result : op->getResults())
3039 (void)setLowering(result, result);
3040 return AlreadyLowered;
3052 if (op->getNumResults() == 1) {
3053 auto resultType = op->getResult(0).getType();
3054 if (type_isa<FIRRTLBaseType>(resultType) &&
3056 (
isExpression(op) || isa<mlir::UnrealizedConversionCastOp>(op))) {
3058 (void)setLowering(op->getResult(0), Value());
3062 op->emitOpError(
"LowerToHW couldn't handle this operation");
3063 return LoweringFailure;
3066LogicalResult FIRRTLLowering::visitExpr(ConstantOp op) {
3069 return setLowering(op, Value());
3071 return setLowering(op, getOrCreateIntConstant(op.getValue()));
3074LogicalResult FIRRTLLowering::visitExpr(SpecialConstantOp op) {
3076 if (isa<ClockType>(op.getType())) {
3077 cst = getOrCreateClockConstant(op.getValue() ? seq::ClockConst::High
3078 :
seq::ClockConst::Low);
3080 cst = getOrCreateIntConstant(APInt( 1, op.getValue()));
3082 return setLowering(op, cst);
3085FailureOr<Value> FIRRTLLowering::lowerSubindex(SubindexOp op, Value input) {
3086 auto iIdx = getOrCreateIntConstant(
3088 firrtl::type_cast<FVectorType>(op.getInput().getType())
3095 if (isa<sv::InOutType>(input.getType()))
3096 result = builder.createOrFold<sv::ArrayIndexInOutOp>(input, iIdx);
3099 if (
auto *definingOp = result.getDefiningOp())
3104FailureOr<Value> FIRRTLLowering::lowerSubaccess(SubaccessOp op, Value input) {
3105 Value valueIdx = getLoweredAndExtOrTruncValue(
3107 UIntType::get(op->getContext(),
3109 firrtl::type_cast<FVectorType>(op.getInput().getType())
3110 .getNumElements())));
3112 op->emitError() <<
"input lowering failed";
3119 if (isa<sv::InOutType>(input.getType()))
3120 result = builder.createOrFold<sv::ArrayIndexInOutOp>(input, valueIdx);
3122 result = createArrayIndexing(input, valueIdx);
3123 if (
auto *definingOp = result.getDefiningOp())
3128FailureOr<Value> FIRRTLLowering::lowerSubfield(SubfieldOp op, Value input) {
3129 auto resultType =
lowerType(op->getResult(0).getType());
3130 if (!resultType || !input) {
3131 op->emitError() <<
"subfield type lowering failed";
3137 auto field = firrtl::type_cast<BundleType>(op.getInput().getType())
3138 .getElementName(op.getFieldIndex());
3140 if (isa<sv::InOutType>(input.getType()))
3141 result = builder.createOrFold<sv::StructFieldInOutOp>(input, field);
3144 if (
auto *definingOp = result.getDefiningOp())
3149LogicalResult FIRRTLLowering::visitExpr(SubindexOp op) {
3151 return setLowering(op, Value());
3153 auto input = getPossiblyInoutLoweredValue(op.getInput());
3155 return op.emitError() <<
"input lowering failed";
3157 auto result = lowerSubindex(op, input);
3160 return setLowering(op, *result);
3163LogicalResult FIRRTLLowering::visitExpr(SubaccessOp op) {
3165 return setLowering(op, Value());
3167 auto input = getPossiblyInoutLoweredValue(op.getInput());
3169 return op.emitError() <<
"input lowering failed";
3171 auto result = lowerSubaccess(op, input);
3174 return setLowering(op, *result);
3177LogicalResult FIRRTLLowering::visitExpr(SubfieldOp op) {
3180 if (getLoweredValue(op) || !op.getInput())
3184 return setLowering(op, Value());
3186 auto input = getPossiblyInoutLoweredValue(op.getInput());
3188 return op.emitError() <<
"input lowering failed";
3190 auto result = lowerSubfield(op, input);
3193 return setLowering(op, *result);
3196LogicalResult FIRRTLLowering::visitExpr(VectorCreateOp op) {
3197 auto resultType =
lowerType(op.getResult().getType());
3198 SmallVector<Value> operands;
3200 for (
auto oper :
llvm::reverse(op.getOperands())) {
3201 auto val = getLoweredValue(oper);
3204 operands.push_back(val);
3206 return setLoweringTo<hw::ArrayCreateOp>(op, resultType, operands);
3209LogicalResult FIRRTLLowering::visitExpr(BundleCreateOp op) {
3210 auto resultType =
lowerType(op.getResult().getType());
3211 SmallVector<Value> operands;
3212 for (
auto oper : op.getOperands()) {
3213 auto val = getLoweredValue(oper);
3216 operands.push_back(val);
3218 return setLoweringTo<hw::StructCreateOp>(op, resultType, operands);
3221LogicalResult FIRRTLLowering::visitExpr(FEnumCreateOp op) {
3224 return setLowering(op, Value());
3226 auto input = getLoweredValue(op.getInput());
3227 auto tagName = op.getFieldNameAttr();
3230 if (
auto structType = dyn_cast<hw::StructType>(type)) {
3231 auto enumType = structType.getFieldType(
"tag");
3232 auto enumAttr = hw::EnumFieldAttr::get(op.getLoc(), tagName, enumType);
3233 auto enumOp = builder.create<hw::EnumConstantOp>(enumAttr);
3234 auto unionType = structType.getFieldType(
"body");
3235 auto unionOp = builder.create<hw::UnionCreateOp>(unionType, tagName, input);
3236 SmallVector<Value> operands = {enumOp.getResult(), unionOp.getResult()};
3237 return setLoweringTo<hw::StructCreateOp>(op, structType, operands);
3240 return setLoweringTo<hw::EnumConstantOp>(
3241 op, hw::EnumFieldAttr::get(op.getLoc(), tagName, type));
3244LogicalResult FIRRTLLowering::visitExpr(AggregateConstantOp op) {
3245 auto resultType =
lowerType(op.getResult().getType());
3247 getOrCreateAggregateConstantAttribute(op.getFieldsAttr(), resultType);
3249 return setLoweringTo<hw::AggregateConstantOp>(op, resultType,
3250 cast<ArrayAttr>(attr));
3253LogicalResult FIRRTLLowering::visitExpr(IsTagOp op) {
3254 auto tagName = op.getFieldNameAttr();
3255 auto lhs = getLoweredValue(op.getInput());
3256 if (isa<hw::StructType>(lhs.getType()))
3258 auto enumField = hw::EnumFieldAttr::get(op.getLoc(), tagName, lhs.getType());
3259 auto rhs = builder.create<hw::EnumConstantOp>(enumField);
3260 return setLoweringTo<hw::EnumCmpOp>(op, lhs, rhs);
3263LogicalResult FIRRTLLowering::visitExpr(SubtagOp op) {
3266 return setLowering(op, Value());
3268 auto tagName = op.getFieldNameAttr();
3269 auto input = getLoweredValue(op.getInput());
3271 return setLoweringTo<hw::UnionExtractOp>(op, field, tagName);
3278LogicalResult FIRRTLLowering::visitDecl(WireOp op) {
3279 auto origResultType = op.getResult().getType();
3283 if (!type_isa<FIRRTLType>(origResultType)) {
3284 createBackedge(op.getResult(), origResultType);
3288 auto resultType =
lowerType(origResultType);
3292 if (resultType.isInteger(0)) {
3293 if (op.getInnerSym())
3294 return op.emitError(
"zero width wire is referenced by name [")
3295 << *op.getInnerSym() <<
"] (e.g. in an XMR) but must be removed";
3296 return setLowering(op.getResult(), Value());
3300 auto innerSym = lowerInnerSymbol(op);
3301 auto name = op.getNameAttr();
3304 auto wire = builder.create<hw::WireOp>(
3305 op.getLoc(), getOrCreateZConstant(resultType), name, innerSym);
3307 if (
auto svAttrs = sv::getSVAttributes(op))
3308 sv::setSVAttributes(wire, svAttrs);
3310 return setLowering(op.getResult(), wire);
3313LogicalResult FIRRTLLowering::visitDecl(VerbatimWireOp op) {
3314 auto resultTy =
lowerType(op.getType());
3317 resultTy = sv::InOutType::get(op.getContext(), resultTy);
3319 SmallVector<Value, 4> operands;
3320 operands.reserve(op.getSubstitutions().size());
3321 for (
auto operand : op.getSubstitutions()) {
3322 auto lowered = getLoweredValue(operand);
3325 operands.push_back(lowered);
3328 ArrayAttr symbols = op.getSymbolsAttr();
3330 symbols = ArrayAttr::get(op.getContext(), {});
3332 return setLoweringTo<sv::VerbatimExprSEOp>(op, resultTy, op.getTextAttr(),
3336LogicalResult FIRRTLLowering::visitDecl(NodeOp op) {
3337 auto operand = getLoweredValue(op.getInput());
3339 return handleZeroBit(op.getInput(), [&]() -> LogicalResult {
3340 if (op.getInnerSym())
3341 return op.emitError(
"zero width node is referenced by name [")
3342 << *op.getInnerSym()
3343 <<
"] (e.g. in an XMR) but must be "
3345 return setLowering(op.getResult(), Value());
3351 auto name = op.getNameAttr();
3352 auto innerSym = lowerInnerSymbol(op);
3355 operand = builder.create<hw::WireOp>(operand, name, innerSym);
3358 if (
auto svAttrs = sv::getSVAttributes(op)) {
3360 operand = builder.create<hw::WireOp>(operand, name);
3361 sv::setSVAttributes(operand.getDefiningOp(), svAttrs);
3364 return setLowering(op.getResult(), operand);
3367LogicalResult FIRRTLLowering::visitDecl(RegOp op) {
3368 auto resultType =
lowerType(op.getResult().getType());
3371 if (resultType.isInteger(0))
3372 return setLowering(op.getResult(), Value());
3374 Value clockVal = getLoweredValue(op.getClockVal());
3379 auto innerSym = lowerInnerSymbol(op);
3380 Backedge inputEdge = backedgeBuilder.
get(resultType);
3381 auto reg = builder.create<seq::FirRegOp>(inputEdge, clockVal,
3382 op.getNameAttr(), innerSym);
3385 if (
auto randomRegister = op->getAttr(
"firrtl.random_init_register"))
3386 reg->setAttr(
"firrtl.random_init_register", randomRegister);
3387 if (
auto randomStart = op->getAttr(
"firrtl.random_init_start"))
3388 reg->setAttr(
"firrtl.random_init_start", randomStart);
3389 if (
auto randomEnd = op->getAttr(
"firrtl.random_init_end"))
3390 reg->setAttr(
"firrtl.random_init_end", randomEnd);
3393 if (
auto svAttrs = sv::getSVAttributes(op))
3394 sv::setSVAttributes(reg, svAttrs);
3396 inputEdge.setValue(reg);
3397 (void)setLowering(op.getResult(),
reg);
3401LogicalResult FIRRTLLowering::visitDecl(RegResetOp op) {
3402 auto resultType =
lowerType(op.getResult().getType());
3405 if (resultType.isInteger(0))
3406 return setLowering(op.getResult(), Value());
3408 Value clockVal = getLoweredValue(op.getClockVal());
3409 Value resetSignal = getLoweredValue(op.getResetSignal());
3411 Value resetValue = getLoweredAndExtOrTruncValue(
3412 op.getResetValue(), type_cast<FIRRTLBaseType>(op.getResult().getType()));
3414 if (!clockVal || !resetSignal || !resetValue)
3418 auto innerSym = lowerInnerSymbol(op);
3419 bool isAsync = type_isa<AsyncResetType>(op.getResetSignal().getType());
3420 Backedge inputEdge = backedgeBuilder.
get(resultType);
3422 builder.create<seq::FirRegOp>(inputEdge, clockVal, op.getNameAttr(),
3423 resetSignal, resetValue, innerSym, isAsync);
3426 if (
auto randomRegister = op->getAttr(
"firrtl.random_init_register"))
3427 reg->setAttr(
"firrtl.random_init_register", randomRegister);
3428 if (
auto randomStart = op->getAttr(
"firrtl.random_init_start"))
3429 reg->setAttr(
"firrtl.random_init_start", randomStart);
3430 if (
auto randomEnd = op->getAttr(
"firrtl.random_init_end"))
3431 reg->setAttr(
"firrtl.random_init_end", randomEnd);
3434 if (
auto svAttrs = sv::getSVAttributes(op))
3435 sv::setSVAttributes(reg, svAttrs);
3437 inputEdge.setValue(reg);
3438 (void)setLowering(op.getResult(),
reg);
3443LogicalResult FIRRTLLowering::visitDecl(MemOp op) {
3446 if (type_isa<BundleType>(op.getDataType()))
3447 return op.emitOpError(
3448 "should have already been lowered from a ground type to an aggregate "
3449 "type using the LowerTypes pass. Use "
3450 "'firtool --lower-types' or 'circt-opt "
3451 "--pass-pipeline='firrtl.circuit(firrtl-lower-types)' "
3457 auto memType = seq::FirMemType::get(
3460 : std::optional<uint32_t>());
3462 seq::FirMemInitAttr memInit;
3463 if (
auto init = op.getInitAttr())
3464 memInit = seq::FirMemInitAttr::get(init.getContext(), init.getFilename(),
3465 init.getIsBinary(), init.getIsInline());
3467 auto memDecl = builder.create<seq::FirMemOp>(
3470 op.getInnerSymAttr(), memInit, op.getPrefixAttr(), Attribute{});
3474 if (!circuitState.isInDUT(theModule))
3475 if (
auto testBenchDir = circuitState.getTestBenchDirectory())
3476 memDecl.setOutputFileAttr(testBenchDir);
3480 for (
size_t i = 0, e = op.getNumResults(); i != e; ++i) {
3482 auto addOutput = [&](StringRef field,
size_t width, Value value) {
3485 (void)setLowering(a, value);
3491 auto addInput = [&](StringRef field, Value backedge) {
3493 if (cast<FIRRTLBaseType>(a.getType())
3495 .getBitWidthOrSentinel() > 0)
3496 (void)setLowering(a, backedge);
3502 auto addInputPort = [&](StringRef field,
size_t width) -> Value {
3506 Value backedge, portValue;
3508 portValue = getOrCreateXConstant(1);
3510 auto portType = IntegerType::get(op.getContext(), width);
3511 backedge = portValue = createBackedge(builder.getLoc(), portType);
3513 addInput(field, backedge);
3517 auto addClock = [&](StringRef field) -> Value {
3518 Type clockTy = seq::ClockType::get(op.getContext());
3519 Value portValue = createBackedge(builder.getLoc(), clockTy);
3520 addInput(field, portValue);
3524 auto memportKind = op.getPortKind(i);
3525 if (memportKind == MemOp::PortKind::Read) {
3526 auto addr = addInputPort(
"addr", op.getAddrBits());
3527 auto en = addInputPort(
"en", 1);
3528 auto clk = addClock(
"clk");
3529 auto data = builder.create<seq::FirMemReadOp>(memDecl,
addr,
clk,
en);
3531 }
else if (memportKind == MemOp::PortKind::ReadWrite) {
3532 auto addr = addInputPort(
"addr", op.getAddrBits());
3533 auto en = addInputPort(
"en", 1);
3534 auto clk = addClock(
"clk");
3537 auto mode = addInputPort(
"wmode", 1);
3539 mode = builder.createOrFold<
comb::AndOp>(mode, addInputPort(
"wmask", 1),
3546 auto rdata = builder.create<seq::FirMemReadWriteOp>(
3550 auto addr = addInputPort(
"addr", op.getAddrBits());
3553 auto en = addInputPort(
"en", 1);
3557 auto clk = addClock(
"clk");
3570LogicalResult FIRRTLLowering::visitDecl(InstanceOp oldInstance) {
3571 Operation *oldModule =
3572 oldInstance.getReferencedModule(circuitState.getInstanceGraph());
3574 auto *newModule = circuitState.getNewModule(oldModule);
3576 oldInstance->emitOpError(
"could not find module [")
3577 << oldInstance.getModuleName() <<
"] referenced by instance";
3583 ArrayAttr parameters;
3584 if (
auto oldExtModule = dyn_cast<FExtModuleOp>(oldModule))
3589 SmallVector<PortInfo, 8> portInfo = cast<FModuleLike>(oldModule).getPorts();
3594 for (
unsigned portIdx = 0, e = portInfo.size(); portIdx != e; ++portIdx)
3595 portIndicesByName[portInfo[portIdx].name] = portIdx;
3599 SmallVector<Value, 8> operands;
3600 for (
size_t portIndex = 0, e = portInfo.size(); portIndex != e; ++portIndex) {
3601 auto &port = portInfo[portIndex];
3604 oldInstance->emitOpError(
"could not lower type of port ") << port.name;
3609 if (portType.isInteger(0))
3613 if (port.isOutput())
3616 auto portResult = oldInstance.getResult(portIndex);
3617 assert(portResult &&
"invalid IR, couldn't find port");
3621 if (port.isInput()) {
3622 operands.push_back(createBackedge(portResult, portType));
3628 if (type_isa<AnalogType>(portResult.getType()) && portResult.hasOneUse()) {
3629 if (
auto attach = dyn_cast<AttachOp>(*portResult.getUsers().begin())) {
3631 auto loweredResult = getPossiblyInoutLoweredValue(source);
3632 operands.push_back(loweredResult);
3633 (void)setLowering(portResult, loweredResult);
3642 portType,
"." + port.getName().str() +
".wire");
3646 (void)setLowering(portResult, wire);
3648 operands.push_back(wire);
3655 auto innerSym = oldInstance.getInnerSymAttr();
3656 if (oldInstance.getLowerToBind()) {
3659 oldInstance.getContext(), oldInstance.getInnerSymAttr(), 0,
3662 auto bindOp = builder.create<sv::BindOp>(theModule.getNameAttr(),
3663 innerSym.getSymName());
3666 if (
auto outputFile = oldInstance->getAttr(
"output_file"))
3667 bindOp->setAttr(
"output_file", outputFile);
3670 circuitState.addBind(bindOp);
3674 auto newInstance = builder.create<hw::InstanceOp>(
3675 newModule, oldInstance.getNameAttr(), operands, parameters, innerSym);
3677 if (oldInstance.getLowerToBind() || oldInstance.getDoNotPrint())
3678 newInstance.setDoNotPrintAttr(builder.getUnitAttr());
3680 if (newInstance.getInnerSymAttr())
3681 if (
auto forceName = circuitState.instanceForceNames.lookup(
3682 {cast<hw::HWModuleOp>(newInstance->getParentOp()).getNameAttr(),
3683 newInstance.getInnerNameAttr()}))
3684 newInstance->setAttr(
"hw.verilogName", forceName);
3688 unsigned resultNo = 0;
3689 for (
size_t portIndex = 0, e = portInfo.size(); portIndex != e; ++portIndex) {
3690 auto &port = portInfo[portIndex];
3694 Value resultVal = newInstance.getResult(resultNo);
3696 auto oldPortResult = oldInstance.getResult(portIndex);
3697 (void)setLowering(oldPortResult, resultVal);
3703LogicalResult FIRRTLLowering::visitDecl(ContractOp oldOp) {
3704 SmallVector<Value> inputs;
3705 SmallVector<Type> types;
3706 for (
auto input : oldOp.getInputs()) {
3707 auto lowered = getLoweredValue(input);
3710 inputs.push_back(lowered);
3711 types.push_back(lowered.getType());
3714 auto newOp = builder.create<verif::ContractOp>(types, inputs);
3715 newOp->setDiscardableAttrs(oldOp->getDiscardableAttrDictionary());
3716 auto &body = newOp.getBody().emplaceBlock();
3718 for (
auto [newResult, oldResult, oldArg] :
3719 llvm::zip(newOp.getResults(), oldOp.getResults(),
3720 oldOp.getBody().getArguments())) {
3721 if (failed(setLowering(oldResult, newResult)))
3723 if (failed(setLowering(oldArg, newResult)))
3727 body.getOperations().splice(body.end(),
3728 oldOp.getBody().front().getOperations());
3729 addToWorklist(body);
3739LogicalResult FIRRTLLowering::lowerNoopCast(Operation *op) {
3740 auto operand = getPossiblyInoutLoweredValue(op->getOperand(0));
3745 return setLowering(op->getResult(0), operand);
3748LogicalResult FIRRTLLowering::visitExpr(AsSIntPrimOp op) {
3749 if (isa<ClockType>(op.getInput().getType()))
3750 return setLowering(op->getResult(0),
3751 getLoweredNonClockValue(op.getInput()));
3752 return lowerNoopCast(op);
3755LogicalResult FIRRTLLowering::visitExpr(AsUIntPrimOp op) {
3756 if (isa<ClockType>(op.getInput().getType()))
3757 return setLowering(op->getResult(0),
3758 getLoweredNonClockValue(op.getInput()));
3759 return lowerNoopCast(op);
3762LogicalResult FIRRTLLowering::visitExpr(AsClockPrimOp op) {
3763 return setLoweringTo<seq::ToClockOp>(op, getLoweredValue(op.getInput()));
3766LogicalResult FIRRTLLowering::visitUnrealizedConversionCast(
3767 mlir::UnrealizedConversionCastOp op) {
3769 if (op.getNumOperands() != 1 || op.getNumResults() != 1)
3772 auto operand = op.getOperand(0);
3773 auto result = op.getResult(0);
3776 if (type_isa<FIRRTLType>(operand.getType()) &&
3777 type_isa<FIRRTLType>(result.getType()))
3778 return lowerNoopCast(op);
3782 if (!type_isa<FIRRTLType>(operand.getType())) {
3783 if (type_isa<FIRRTLType>(result.getType()))
3784 return setLowering(result, getPossiblyInoutLoweredValue(operand));
3790 auto loweredResult = getLoweredValue(operand);
3791 if (!loweredResult) {
3794 if (operand.getType().isSignlessInteger(0)) {
3795 return setLowering(result, Value());
3802 result.replaceAllUsesWith(loweredResult);
3806LogicalResult FIRRTLLowering::visitExpr(HWStructCastOp op) {
3809 if (
auto opStructType = dyn_cast<hw::StructType>(op.getOperand().getType()))
3810 return setLowering(op, op.getOperand());
3814 auto result = getLoweredValue(op.getOperand());
3820 op.replaceAllUsesWith(result);
3824LogicalResult FIRRTLLowering::visitExpr(BitCastOp op) {
3825 auto operand = getLoweredValue(op.getOperand());
3828 auto resultType =
lowerType(op.getType());
3832 return setLoweringTo<hw::BitcastOp>(op, resultType, operand);
3835LogicalResult FIRRTLLowering::visitExpr(CvtPrimOp op) {
3836 auto operand = getLoweredValue(op.getOperand());
3840 if (type_cast<IntType>(op.getOperand().getType()).isUnsigned())
3841 return setLowering(op, getOrCreateIntConstant(1, 0));
3843 return setLowering(op, Value());
3848 if (type_cast<IntType>(op.getOperand().getType()).isSigned())
3849 return setLowering(op, operand);
3852 auto zero = getOrCreateIntConstant(1, 0);
3853 return setLoweringTo<comb::ConcatOp>(op, zero, operand);
3856LogicalResult FIRRTLLowering::visitExpr(NotPrimOp op) {
3857 auto operand = getLoweredValue(op.getInput());
3861 auto allOnes = getOrCreateIntConstant(
3862 APInt::getAllOnes(operand.getType().getIntOrFloatBitWidth()));
3863 return setLoweringTo<comb::XorOp>(op, operand, allOnes,
true);
3866LogicalResult FIRRTLLowering::visitExpr(NegPrimOp op) {
3869 auto operand = getLoweredAndExtendedValue(op.getInput(), op.getType());
3873 auto resultType =
lowerType(op.getType());
3875 auto zero = getOrCreateIntConstant(resultType.getIntOrFloatBitWidth(), 0);
3876 return setLoweringTo<comb::SubOp>(op, zero, operand,
true);
3880LogicalResult FIRRTLLowering::visitExpr(PadPrimOp op) {
3881 auto operand = getLoweredAndExtendedValue(op.getInput(), op.getType());
3884 return setLowering(op, operand);
3887LogicalResult FIRRTLLowering::visitExpr(XorRPrimOp op) {
3888 auto operand = getLoweredValue(op.getInput());
3891 return setLowering(op, getOrCreateIntConstant(1, 0));
3896 return setLoweringTo<comb::ParityOp>(op, builder.getIntegerType(1), operand,
3900LogicalResult FIRRTLLowering::visitExpr(AndRPrimOp op) {
3901 auto operand = getLoweredValue(op.getInput());
3904 return setLowering(op, getOrCreateIntConstant(1, 1));
3909 return setLoweringTo<comb::ICmpOp>(
3910 op, ICmpPredicate::eq, operand,
3911 getOrCreateIntConstant(
3912 APInt::getAllOnes(operand.getType().getIntOrFloatBitWidth())),
3916LogicalResult FIRRTLLowering::visitExpr(OrRPrimOp op) {
3917 auto operand = getLoweredValue(op.getInput());
3920 return setLowering(op, getOrCreateIntConstant(1, 0));
3926 return setLoweringTo<comb::ICmpOp>(
3927 op, ICmpPredicate::ne, operand,
3928 getOrCreateIntConstant(operand.getType().getIntOrFloatBitWidth(), 0),
3936template <
typename ResultOpType>
3937LogicalResult FIRRTLLowering::lowerBinOpToVariadic(Operation *op) {
3938 auto resultType = op->getResult(0).getType();
3939 auto lhs = getLoweredAndExtendedValue(op->getOperand(0), resultType);
3940 auto rhs = getLoweredAndExtendedValue(op->getOperand(1), resultType);
3944 return setLoweringTo<ResultOpType>(op, lhs, rhs,
true);
3950template <
typename ResultOpType>
3951LogicalResult FIRRTLLowering::lowerElementwiseLogicalOp(Operation *op) {
3952 auto resultType = op->getResult(0).getType();
3953 auto lhs = getLoweredAndExtendedValue(op->getOperand(0), resultType);
3954 auto rhs = getLoweredAndExtendedValue(op->getOperand(1), resultType);
3965 auto intType = builder.getIntegerType(*bitwidth);
3966 auto retType = lhs.getType();
3969 auto result = builder.createOrFold<ResultOpType>(lhs, rhs,
true);
3970 return setLoweringTo<hw::BitcastOp>(op, retType, result);
3975template <
typename ResultUn
signedOpType,
typename ResultSignedOpType>
3976LogicalResult FIRRTLLowering::lowerBinOp(Operation *op) {
3978 auto resultType = op->getResult(0).getType();
3979 auto lhs = getLoweredAndExtendedValue(op->getOperand(0), resultType);
3980 auto rhs = getLoweredAndExtendedValue(op->getOperand(1), resultType);
3985 if (type_cast<IntType>(resultType).isSigned())
3986 return setLoweringTo<ResultSignedOpType>(op, lhs, rhs,
true);
3987 return setLoweringTo<ResultUnsignedOpType>(op, lhs, rhs,
true);
3992LogicalResult FIRRTLLowering::lowerCmpOp(Operation *op, ICmpPredicate signedOp,
3993 ICmpPredicate unsignedOp) {
3995 auto lhsIntType = type_cast<IntType>(op->getOperand(0).getType());
3996 auto rhsIntType = type_cast<IntType>(op->getOperand(1).getType());
3997 if (!lhsIntType.hasWidth() || !rhsIntType.hasWidth())
4001 if (cmpType.getWidth() == 0)
4002 cmpType = UIntType::get(builder.getContext(), 1);
4003 auto lhs = getLoweredAndExtendedValue(op->getOperand(0), cmpType);
4004 auto rhs = getLoweredAndExtendedValue(op->getOperand(1), cmpType);
4009 Type resultType = builder.getIntegerType(1);
4010 return setLoweringTo<comb::ICmpOp>(
4011 op, resultType, lhsIntType.isSigned() ? signedOp : unsignedOp, lhs, rhs,
4017template <
typename SignedOp,
typename Un
signedOp>
4018LogicalResult FIRRTLLowering::lowerDivLikeOp(Operation *op) {
4022 auto opType = type_cast<IntType>(op->getResult(0).getType());
4023 if (opType.getWidth() == 0)
4024 return setLowering(op->getResult(0), Value());
4028 auto lhs = getLoweredAndExtendedValue(op->getOperand(0), resultType);
4029 auto rhs = getLoweredAndExtendedValue(op->getOperand(1), resultType);
4034 if (opType.isSigned())
4035 result = builder.createOrFold<SignedOp>(lhs, rhs,
true);
4037 result = builder.createOrFold<UnsignedOp>(lhs, rhs,
true);
4039 if (
auto *definingOp = result.getDefiningOp())
4042 if (resultType == opType)
4043 return setLowering(op->getResult(0), result);
4044 return setLoweringTo<comb::ExtractOp>(op,
lowerType(opType), result, 0);
4047LogicalResult FIRRTLLowering::visitExpr(CatPrimOp op) {
4048 auto lhs = getLoweredValue(op.getLhs());
4049 auto rhs = getLoweredValue(op.getRhs());
4053 return setLowering(op, rhs);
4055 return handleZeroBit(op.getRhs(),
4056 [&]() { return setLowering(op, Value()); });
4061 return handleZeroBit(op.getRhs(), [&]() { return setLowering(op, lhs); });
4063 return setLoweringTo<comb::ConcatOp>(op, lhs, rhs);
4070LogicalResult FIRRTLLowering::visitExpr(IsXIntrinsicOp op) {
4071 auto input = getLoweredNonClockValue(op.getArg());
4075 if (!isa<IntType>(input.getType())) {
4076 auto srcType = op.getArg().getType();
4078 assert(bitwidth &&
"Unknown width");
4079 auto intType = builder.getIntegerType(*bitwidth);
4080 input = builder.createOrFold<
hw::BitcastOp>(intType, input);
4083 return setLoweringTo<comb::ICmpOp>(
4084 op, ICmpPredicate::ceq, input,
4085 getOrCreateXConstant(input.getType().getIntOrFloatBitWidth()),
true);
4088LogicalResult FIRRTLLowering::visitStmt(FPGAProbeIntrinsicOp op) {
4089 auto operand = getLoweredValue(op.getInput());
4090 builder.create<hw::WireOp>(operand);
4094LogicalResult FIRRTLLowering::visitExpr(PlusArgsTestIntrinsicOp op) {
4095 return setLoweringTo<sim::PlusArgsTestOp>(op, builder.getIntegerType(1),
4096 op.getFormatStringAttr());
4099LogicalResult FIRRTLLowering::visitExpr(PlusArgsValueIntrinsicOp op) {
4100 auto type =
lowerType(op.getResult().getType());
4104 auto valueOp = builder.create<sim::PlusArgsValueOp>(
4105 builder.getIntegerType(1), type, op.getFormatStringAttr());
4106 if (failed(setLowering(op.getResult(), valueOp.getResult())))
4108 if (failed(setLowering(op.getFound(), valueOp.getFound())))
4113LogicalResult FIRRTLLowering::visitExpr(SizeOfIntrinsicOp op) {
4114 op.emitError(
"SizeOf should have been resolved.");
4118LogicalResult FIRRTLLowering::visitExpr(ClockGateIntrinsicOp op) {
4120 if (op.getTestEnable())
4121 testEnable = getLoweredValue(op.getTestEnable());
4122 return setLoweringTo<seq::ClockGateOp>(
4123 op, getLoweredValue(op.getInput()), getLoweredValue(op.getEnable()),
4124 testEnable, hw::InnerSymAttr{});
4127LogicalResult FIRRTLLowering::visitExpr(ClockInverterIntrinsicOp op) {
4128 auto operand = getLoweredValue(op.getInput());
4129 return setLoweringTo<seq::ClockInverterOp>(op, operand);
4132LogicalResult FIRRTLLowering::visitExpr(ClockDividerIntrinsicOp op) {
4133 auto operand = getLoweredValue(op.getInput());
4134 return setLoweringTo<seq::ClockDividerOp>(op, operand, op.getPow2());
4137LogicalResult FIRRTLLowering::visitExpr(LTLAndIntrinsicOp op) {
4138 return setLoweringToLTL<ltl::AndOp>(
4140 ValueRange{getLoweredValue(op.getLhs()), getLoweredValue(op.getRhs())});
4143LogicalResult FIRRTLLowering::visitExpr(LTLOrIntrinsicOp op) {
4144 return setLoweringToLTL<ltl::OrOp>(
4146 ValueRange{getLoweredValue(op.getLhs()), getLoweredValue(op.getRhs())});
4149LogicalResult FIRRTLLowering::visitExpr(LTLIntersectIntrinsicOp op) {
4150 return setLoweringToLTL<ltl::IntersectOp>(
4152 ValueRange{getLoweredValue(op.getLhs()), getLoweredValue(op.getRhs())});
4155LogicalResult FIRRTLLowering::visitExpr(LTLDelayIntrinsicOp op) {
4156 return setLoweringToLTL<ltl::DelayOp>(op, getLoweredValue(op.getInput()),
4157 op.getDelayAttr(), op.getLengthAttr());
4160LogicalResult FIRRTLLowering::visitExpr(LTLConcatIntrinsicOp op) {
4161 return setLoweringToLTL<ltl::ConcatOp>(
4163 ValueRange{getLoweredValue(op.getLhs()), getLoweredValue(op.getRhs())});
4166LogicalResult FIRRTLLowering::visitExpr(LTLRepeatIntrinsicOp op) {
4167 return setLoweringToLTL<ltl::RepeatOp>(op, getLoweredValue(op.getInput()),
4168 op.getBaseAttr(), op.getMoreAttr());
4171LogicalResult FIRRTLLowering::visitExpr(LTLGoToRepeatIntrinsicOp op) {
4172 return setLoweringToLTL<ltl::GoToRepeatOp>(
4173 op, getLoweredValue(op.getInput()), op.getBaseAttr(), op.getMoreAttr());
4176LogicalResult FIRRTLLowering::visitExpr(LTLNonConsecutiveRepeatIntrinsicOp op) {
4177 return setLoweringToLTL<ltl::NonConsecutiveRepeatOp>(
4178 op, getLoweredValue(op.getInput()), op.getBaseAttr(), op.getMoreAttr());
4181LogicalResult FIRRTLLowering::visitExpr(LTLNotIntrinsicOp op) {
4182 return setLoweringToLTL<ltl::NotOp>(op, getLoweredValue(op.getInput()));
4185LogicalResult FIRRTLLowering::visitExpr(LTLImplicationIntrinsicOp op) {
4186 return setLoweringToLTL<ltl::ImplicationOp>(
4188 ValueRange{getLoweredValue(op.getLhs()), getLoweredValue(op.getRhs())});
4191LogicalResult FIRRTLLowering::visitExpr(LTLUntilIntrinsicOp op) {
4192 return setLoweringToLTL<ltl::UntilOp>(
4194 ValueRange{getLoweredValue(op.getLhs()), getLoweredValue(op.getRhs())});
4197LogicalResult FIRRTLLowering::visitExpr(LTLEventuallyIntrinsicOp op) {
4198 return setLoweringToLTL<ltl::EventuallyOp>(op,
4199 getLoweredValue(op.getInput()));
4202LogicalResult FIRRTLLowering::visitExpr(LTLClockIntrinsicOp op) {
4203 return setLoweringToLTL<ltl::ClockOp>(op, getLoweredValue(op.getInput()),
4204 ltl::ClockEdge::Pos,
4205 getLoweredNonClockValue(op.getClock()));
4208template <
typename TargetOp,
typename IntrinsicOp>
4209LogicalResult FIRRTLLowering::lowerVerifIntrinsicOp(IntrinsicOp op) {
4210 auto property = getLoweredValue(op.getProperty());
4211 auto enable = op.getEnable() ? getLoweredValue(op.getEnable()) : Value();
4212 builder.create<TargetOp>(property, enable, op.getLabelAttr());
4216LogicalResult FIRRTLLowering::visitStmt(VerifAssertIntrinsicOp op) {
4217 return lowerVerifIntrinsicOp<verif::AssertOp>(op);
4220LogicalResult FIRRTLLowering::visitStmt(VerifAssumeIntrinsicOp op) {
4221 return lowerVerifIntrinsicOp<verif::AssumeOp>(op);
4224LogicalResult FIRRTLLowering::visitStmt(VerifCoverIntrinsicOp op) {
4225 return lowerVerifIntrinsicOp<verif::CoverOp>(op);
4228LogicalResult FIRRTLLowering::visitStmt(VerifRequireIntrinsicOp op) {
4229 if (!isa<verif::ContractOp>(op->getParentOp()))
4230 return lowerVerifIntrinsicOp<verif::AssertOp>(op);
4231 return lowerVerifIntrinsicOp<verif::RequireOp>(op);
4234LogicalResult FIRRTLLowering::visitStmt(VerifEnsureIntrinsicOp op) {
4235 if (!isa<verif::ContractOp>(op->getParentOp()))
4236 return lowerVerifIntrinsicOp<verif::AssertOp>(op);
4237 return lowerVerifIntrinsicOp<verif::EnsureOp>(op);
4240LogicalResult FIRRTLLowering::visitExpr(HasBeenResetIntrinsicOp op) {
4241 auto clock = getLoweredNonClockValue(op.getClock());
4242 auto reset = getLoweredValue(op.getReset());
4243 if (!clock || !reset)
4245 auto resetType = op.getReset().getType();
4246 auto uintResetType = dyn_cast<UIntType>(resetType);
4247 auto isSync = uintResetType && uintResetType.getWidth() == 1;
4248 auto isAsync = isa<AsyncResetType>(resetType);
4249 if (!isAsync && !isSync) {
4250 auto d = op.emitError(
"uninferred reset passed to 'has_been_reset'; "
4251 "requires sync or async reset");
4252 d.attachNote() <<
"reset is of type " << resetType
4253 <<
", should be '!firrtl.uint<1>' or '!firrtl.asyncreset'";
4256 return setLoweringTo<verif::HasBeenResetOp>(op, clock, reset, isAsync);
4263LogicalResult FIRRTLLowering::visitExpr(BitsPrimOp op) {
4264 auto input = getLoweredValue(op.getInput());
4268 Type resultType = builder.getIntegerType(op.getHi() - op.getLo() + 1);
4269 return setLoweringTo<comb::ExtractOp>(op, resultType, input, op.getLo());
4272LogicalResult FIRRTLLowering::visitExpr(InvalidValueOp op) {
4273 auto resultTy =
lowerType(op.getType());
4280 if (type_isa<AnalogType>(op.getType()))
4283 return setLoweringTo<sv::WireOp>(op, resultTy,
".invalid_analog");
4286 if (type_cast<FIRRTLBaseType>(op.getType()).containsAnalog())
4297 auto constant = getOrCreateIntConstant(*bitwidth, 0);
4299 if (!type_isa<IntegerType>(resultTy))
4300 constant = builder.create<
hw::BitcastOp>(resultTy, constant);
4301 return setLowering(op, constant);
4305 op.emitOpError(
"unsupported type");
4309LogicalResult FIRRTLLowering::visitExpr(HeadPrimOp op) {
4310 auto input = getLoweredValue(op.getInput());
4313 auto inWidth = type_cast<IntegerType>(input.getType()).getWidth();
4314 if (op.getAmount() == 0)
4315 return setLowering(op, Value());
4316 Type resultType = builder.getIntegerType(op.getAmount());
4317 return setLoweringTo<comb::ExtractOp>(op, resultType, input,
4318 inWidth - op.getAmount());
4321LogicalResult FIRRTLLowering::visitExpr(ShlPrimOp op) {
4322 auto input = getLoweredValue(op.getInput());
4325 if (op.getAmount() == 0)
4327 return setLowering(op, getOrCreateIntConstant(op.getAmount(), 0));
4332 if (op.getAmount() == 0)
4333 return setLowering(op, input);
4335 auto zero = getOrCreateIntConstant(op.getAmount(), 0);
4336 return setLoweringTo<comb::ConcatOp>(op, input, zero);
4339LogicalResult FIRRTLLowering::visitExpr(ShrPrimOp op) {
4340 auto input = getLoweredValue(op.getInput());
4345 auto inWidth = type_cast<IntegerType>(input.getType()).getWidth();
4346 auto shiftAmount = op.getAmount();
4347 if (shiftAmount >= inWidth) {
4349 if (type_cast<IntType>(op.getInput().getType()).isUnsigned())
4350 return setLowering(op, {});
4353 shiftAmount = inWidth - 1;
4356 Type resultType = builder.getIntegerType(inWidth - shiftAmount);
4357 return setLoweringTo<comb::ExtractOp>(op, resultType, input, shiftAmount);
4360LogicalResult FIRRTLLowering::visitExpr(TailPrimOp op) {
4361 auto input = getLoweredValue(op.getInput());
4365 auto inWidth = type_cast<IntegerType>(input.getType()).getWidth();
4366 if (inWidth == op.getAmount())
4367 return setLowering(op, Value());
4368 Type resultType = builder.getIntegerType(inWidth - op.getAmount());
4369 return setLoweringTo<comb::ExtractOp>(op, resultType, input, 0);
4372LogicalResult FIRRTLLowering::visitExpr(MuxPrimOp op) {
4373 auto cond = getLoweredValue(op.getSel());
4374 auto ifTrue = getLoweredAndExtendedValue(op.getHigh(), op.getType());
4375 auto ifFalse = getLoweredAndExtendedValue(op.getLow(), op.getType());
4376 if (!cond || !ifTrue || !ifFalse)
4379 if (isa<ClockType>(op.getType()))
4380 return setLoweringTo<seq::ClockMuxOp>(op, cond, ifTrue, ifFalse);
4381 return setLoweringTo<comb::MuxOp>(op, ifTrue.getType(), cond, ifTrue, ifFalse,
4385LogicalResult FIRRTLLowering::visitExpr(Mux2CellIntrinsicOp 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 auto val = builder.create<
comb::MuxOp>(ifTrue.getType(), cond, ifTrue,
4394 return setLowering(op, createValueWithMuxAnnotation(val,
true));
4397LogicalResult FIRRTLLowering::visitExpr(Mux4CellIntrinsicOp op) {
4398 auto sel = getLoweredValue(op.getSel());
4399 auto v3 = getLoweredAndExtendedValue(op.getV3(), op.getType());
4400 auto v2 = getLoweredAndExtendedValue(op.getV2(), op.getType());
4401 auto v1 = getLoweredAndExtendedValue(op.getV1(), op.getType());
4402 auto v0 = getLoweredAndExtendedValue(op.getV0(), op.getType());
4403 if (!sel || !v3 || !v2 || !v1 || !v0)
4405 Value array[] = {v3, v2, v1, v0};
4408 return setLowering(op, createValueWithMuxAnnotation(val,
false));
4427Value FIRRTLLowering::createValueWithMuxAnnotation(Operation *op,
bool isMux2) {
4428 assert(op->getNumResults() == 1 &&
"only expect a single result");
4429 auto val = op->getResult(0);
4433 op, sv::SVAttributeAttr::get(builder.getContext(),
"cadence map_to_mux",
4440 OpBuilder::InsertionGuard guard(builder);
4441 builder.setInsertionPoint(op);
4442 StringRef namehint = isMux2 ?
"mux2cell_in" :
"mux4cell_in";
4443 for (
auto [idx, operand] :
llvm::enumerate(op->getOperands())) {
4445 op->getContext(),
nullptr, 0,
4448 builder.create<hw::WireOp>(operand, namehint + Twine(idx), innerSym);
4449 op->setOperand(idx, wire);
4453 auto assignOp = builder.create<
sv::AssignOp>(valWire, val);
4454 sv::setSVAttributes(assignOp,
4455 sv::SVAttributeAttr::get(builder.getContext(),
4456 "synopsys infer_mux_override",
4461Value FIRRTLLowering::createArrayIndexing(Value array, Value index) {
4463 auto size = hw::type_cast<hw::ArrayType>(array.getType()).getNumElements();
4468 if (!llvm::isPowerOf2_64(size)) {
4469 auto extElem = getOrCreateIntConstant(APInt(llvm::Log2_64_Ceil(size), 0));
4471 SmallVector<Value> temp(llvm::NextPowerOf2(size) - size, extValue);
4473 Value temp2[] = {ext.getResult(), array};
4479 return inBoundsRead;
4482LogicalResult FIRRTLLowering::visitExpr(MultibitMuxOp op) {
4484 auto index = getLoweredAndExtOrTruncValue(
4486 UIntType::get(op.getContext(),
4491 SmallVector<Value> loweredInputs;
4492 loweredInputs.reserve(op.getInputs().size());
4493 for (
auto input : op.getInputs()) {
4494 auto lowered = getLoweredAndExtendedValue(input, op.getType());
4497 loweredInputs.push_back(lowered);
4501 return setLowering(op, createArrayIndexing(array, index));
4504LogicalResult FIRRTLLowering::visitExpr(VerbatimExprOp op) {
4505 auto resultTy =
lowerType(op.getType());
4509 SmallVector<Value, 4> operands;
4510 operands.reserve(op.getSubstitutions().size());
4511 for (
auto operand : op.getSubstitutions()) {
4512 auto lowered = getLoweredValue(operand);
4515 operands.push_back(lowered);
4518 ArrayAttr symbols = op.getSymbolsAttr();
4520 symbols = ArrayAttr::get(op.getContext(), {});
4522 return setLoweringTo<sv::VerbatimExprOp>(op, resultTy, op.getTextAttr(),
4526LogicalResult FIRRTLLowering::visitExpr(XMRRefOp op) {
4530 Type baseType = op.getType().getType();
4533 if (isa<ClockType>(baseType))
4534 xmrType = builder.getIntegerType(1);
4538 return setLoweringTo<sv::XMRRefOp>(op, sv::InOutType::get(xmrType),
4539 op.getRef(), op.getVerbatimSuffixAttr());
4542LogicalResult FIRRTLLowering::visitExpr(XMRDerefOp op) {
4546 if (isa<ClockType>(op.getType()))
4547 xmrType = builder.getIntegerType(1);
4551 auto xmr = builder.create<sv::XMRRefOp>(
4552 sv::InOutType::get(xmrType), op.getRef(), op.getVerbatimSuffixAttr());
4553 auto readXmr = getReadValue(xmr);
4554 if (!isa<ClockType>(op.getType()))
4555 return setLowering(op, readXmr);
4556 return setLoweringTo<seq::ToClockOp>(op, readXmr);
4561LogicalResult FIRRTLLowering::visitExpr(TimeOp op) {
return success(); }
4562LogicalResult FIRRTLLowering::visitExpr(HierarchicalModuleNameOp op) {
4570LogicalResult FIRRTLLowering::visitStmt(SkipOp op) {
4582FailureOr<bool> FIRRTLLowering::lowerConnect(Value destVal, Value srcVal) {
4583 auto srcType = srcVal.getType();
4584 auto dstType = destVal.getType();
4585 if (srcType != dstType &&
4586 (isa<hw::TypeAliasType>(srcType) || isa<hw::TypeAliasType>(dstType))) {
4587 srcVal = builder.create<
hw::BitcastOp>(destVal.getType(), srcVal);
4589 return TypeSwitch<Operation *, FailureOr<bool>>(destVal.getDefiningOp())
4590 .Case<hw::WireOp>([&](
auto op) {
4591 maybeUnused(op.getInput());
4592 op.getInputMutable().assign(srcVal);
4595 .Case<seq::FirRegOp>([&](
auto op) {
4596 maybeUnused(op.getNext());
4597 op.getNextMutable().assign(srcVal);
4600 .Case<hw::StructExtractOp, hw::ArrayGetOp>([](
auto op) {
4603 op.emitOpError(
"used as connect destination");
4606 .Default([](
auto) {
return false; });
4609LogicalResult FIRRTLLowering::visitStmt(ConnectOp op) {
4610 auto dest = op.getDest();
4612 auto destType = type_cast<FIRRTLBaseType>(dest.getType()).getPassiveType();
4613 auto srcVal = getLoweredAndExtendedValue(op.getSrc(), destType);
4615 return handleZeroBit(op.getSrc(), []() { return success(); });
4617 auto destVal = getPossiblyInoutLoweredValue(dest);
4621 auto result = lowerConnect(destVal, srcVal);
4629 if (updateIfBackedge(destVal, srcVal))
4632 if (!isa<hw::InOutType>(destVal.getType()))
4633 return op.emitError(
"destination isn't an inout type");
4639LogicalResult FIRRTLLowering::visitStmt(MatchingConnectOp op) {
4640 auto dest = op.getDest();
4641 auto srcVal = getLoweredValue(op.getSrc());
4643 return handleZeroBit(op.getSrc(), []() { return success(); });
4645 auto destVal = getPossiblyInoutLoweredValue(dest);
4649 auto result = lowerConnect(destVal, srcVal);
4657 if (updateIfBackedge(destVal, srcVal))
4660 if (!isa<hw::InOutType>(destVal.getType()))
4661 return op.emitError(
"destination isn't an inout type");
4667LogicalResult FIRRTLLowering::visitStmt(ForceOp op) {
4668 auto srcVal = getLoweredValue(op.getSrc());
4672 auto destVal = getPossiblyInoutLoweredValue(op.getDest());
4676 if (!isa<hw::InOutType>(destVal.getType()))
4677 return op.emitError(
"destination isn't an inout type");
4680 circuitState.addMacroDecl(builder.getStringAttr(
"SYNTHESIS"));
4681 addToIfDefBlock(
"SYNTHESIS", std::function<
void()>(), [&]() {
4682 addToInitialBlock([&]() { builder.create<sv::ForceOp>(destVal, srcVal); });
4687LogicalResult FIRRTLLowering::visitStmt(RefForceOp op) {
4688 auto src = getLoweredNonClockValue(op.getSrc());
4689 auto clock = getLoweredNonClockValue(op.getClock());
4690 auto pred = getLoweredValue(op.getPredicate());
4691 if (!src || !clock || !pred)
4694 auto destVal = getPossiblyInoutLoweredValue(op.getDest());
4699 circuitState.addMacroDecl(builder.getStringAttr(
"SYNTHESIS"));
4700 addToIfDefBlock(
"SYNTHESIS", std::function<
void()>(), [&]() {
4701 addToAlwaysBlock(clock, [&]() {
4702 addIfProceduralBlock(
4703 pred, [&]() { builder.create<sv::ForceOp>(destVal, src); });
4708LogicalResult FIRRTLLowering::visitStmt(RefForceInitialOp op) {
4709 auto src = getLoweredNonClockValue(op.getSrc());
4710 auto pred = getLoweredValue(op.getPredicate());
4714 auto destVal = getPossiblyInoutLoweredValue(op.getDest());
4719 circuitState.addMacroDecl(builder.getStringAttr(
"SYNTHESIS"));
4720 addToIfDefBlock(
"SYNTHESIS", std::function<
void()>(), [&]() {
4721 addToInitialBlock([&]() {
4722 addIfProceduralBlock(
4723 pred, [&]() { builder.create<sv::ForceOp>(destVal, src); });
4728LogicalResult FIRRTLLowering::visitStmt(RefReleaseOp op) {
4729 auto clock = getLoweredNonClockValue(op.getClock());
4730 auto pred = getLoweredValue(op.getPredicate());
4731 if (!clock || !pred)
4734 auto destVal = getPossiblyInoutLoweredValue(op.getDest());
4739 circuitState.addMacroDecl(builder.getStringAttr(
"SYNTHESIS"));
4740 addToIfDefBlock(
"SYNTHESIS", std::function<
void()>(), [&]() {
4741 addToAlwaysBlock(clock, [&]() {
4742 addIfProceduralBlock(pred,
4743 [&]() { builder.create<sv::ReleaseOp>(destVal); });
4748LogicalResult FIRRTLLowering::visitStmt(RefReleaseInitialOp op) {
4749 auto destVal = getPossiblyInoutLoweredValue(op.getDest());
4750 auto pred = getLoweredValue(op.getPredicate());
4751 if (!destVal || !pred)
4755 circuitState.addMacroDecl(builder.getStringAttr(
"SYNTHESIS"));
4756 addToIfDefBlock(
"SYNTHESIS", std::function<
void()>(), [&]() {
4757 addToInitialBlock([&]() {
4758 addIfProceduralBlock(pred,
4759 [&]() { builder.create<sv::ReleaseOp>(destVal); });
4767 StringRef originalFormatString,
4768 mlir::OperandRange operands,
4769 StringAttr &result) {
4772 SmallString<32> formatString;
4773 for (
size_t i = 0, e = originalFormatString.size(), subIdx = 0; i != e; ++i) {
4774 char c = originalFormatString[i];
4778 formatString.push_back(c);
4781 SmallString<6> width;
4782 c = originalFormatString[++i];
4785 c = originalFormatString[++i];
4796 formatString.append(width);
4802 formatString.push_back(c);
4809 if (originalFormatString.slice(i, i + 4) !=
"{{}}") {
4810 formatString.push_back(c);
4814 auto substitution = operands[subIdx++];
4815 assert(type_isa<FStringType>(substitution.getType()) &&
4816 "the operand for a '{{}}' substitution must be an 'fstring' type");
4818 TypeSwitch<Operation *, LogicalResult>(substitution.getDefiningOp())
4819 .template Case<TimeOp>([&](
auto) {
4820 formatString.append(
"%0t");
4823 .
template Case<HierarchicalModuleNameOp>([&](
auto) {
4824 formatString.append(
"%m");
4827 .Default([&](
auto) {
4828 emitError(loc,
"has a substitution with an unimplemented "
4830 .attachNote(substitution.getLoc())
4831 <<
"op with an unimplemented lowering is here";
4841 formatString.push_back(c);
4845 result = StringAttr::get(loc->getContext(), formatString);
4852LogicalResult FIRRTLLowering::visitPrintfLike(
4853 T op,
const FileDescriptorInfo &fileDescriptorInfo,
bool usePrintfCond) {
4854 auto clock = getLoweredNonClockValue(op.getClock());
4855 auto cond = getLoweredValue(op.getCond());
4856 if (!clock || !cond)
4859 StringAttr formatString;
4861 op.getSubstitutions(), formatString)))
4864 auto fn = [&](Value fd) {
4865 SmallVector<Value> operands;
4866 if (failed(loweredFmtOperands(op.getSubstitutions(), operands)))
4868 builder.create<sv::FWriteOp>(op.getLoc(), fd, formatString, operands);
4872 return lowerStatementWithFd(fileDescriptorInfo, clock, cond, fn,
4876LogicalResult FIRRTLLowering::visitStmt(FPrintFOp op) {
4877 StringAttr outputFileAttr;
4879 op.getOutputFileSubstitutions(),
4883 FileDescriptorInfo outputFile(outputFileAttr,
4884 op.getOutputFileSubstitutions());
4885 return visitPrintfLike(op, outputFile,
false);
4889LogicalResult FIRRTLLowering::visitStmt(FFlushOp op) {
4890 auto clock = getLoweredNonClockValue(op.getClock());
4891 auto cond = getLoweredValue(op.getCond());
4892 if (!clock || !cond)
4895 auto fn = [&](Value fd) {
4896 builder.create<sv::FFlushOp>(op.getLoc(), fd);
4900 if (!op.getOutputFileAttr())
4901 return lowerStatementWithFd({}, clock, cond, fn,
false);
4905 StringAttr outputFileAttr;
4907 op.getOutputFileSubstitutions(),
4911 return lowerStatementWithFd(
4912 FileDescriptorInfo(outputFileAttr, op.getOutputFileSubstitutions()),
4913 clock, cond, fn,
false);
4918LogicalResult FIRRTLLowering::visitStmt(StopOp op) {
4919 auto clock = getLoweredValue(op.getClock());
4920 auto cond = getLoweredValue(op.getCond());
4921 if (!clock || !cond)
4924 circuitState.usedStopCond =
true;
4925 circuitState.addFragment(theModule,
"STOP_COND_FRAGMENT");
4928 builder.create<sv::MacroRefExprOp>(cond.getType(),
"STOP_COND_");
4929 Value exitCond = builder.createOrFold<
comb::AndOp>(stopCond, cond,
true);
4931 if (op.getExitCode())
4932 builder.create<sim::FatalOp>(clock, exitCond);
4934 builder.create<sim::FinishOp>(clock, exitCond);
4942template <
typename... Args>
4944 StringRef opName, Args &&...args) {
4945 if (opName ==
"assert")
4946 return builder.create<sv::AssertOp>(std::forward<Args>(args)...);
4947 if (opName ==
"assume")
4948 return builder.create<sv::AssumeOp>(std::forward<Args>(args)...);
4949 if (opName ==
"cover")
4950 return builder.create<sv::CoverOp>(std::forward<Args>(args)...);
4951 llvm_unreachable(
"unknown verification op");
4957template <
typename... Args>
4959 StringRef opName, Args &&...args) {
4960 if (opName ==
"assert")
4961 return builder.create<sv::AssertConcurrentOp>(std::forward<Args>(args)...);
4962 if (opName ==
"assume")
4963 return builder.create<sv::AssumeConcurrentOp>(std::forward<Args>(args)...);
4964 if (opName ==
"cover")
4965 return builder.create<sv::CoverConcurrentOp>(std::forward<Args>(args)...);
4966 llvm_unreachable(
"unknown verification op");
4987LogicalResult FIRRTLLowering::lowerVerificationStatement(
4988 Operation *op, StringRef labelPrefix, Value opClock, Value opPredicate,
4989 Value opEnable, StringAttr opMessageAttr, ValueRange opOperands,
4990 StringAttr opNameAttr,
bool isConcurrent, EventControl opEventControl) {
4991 StringRef opName = op->getName().stripDialect();
4994 ArrayRef<Attribute> guards{};
4995 if (
auto guardsAttr = op->template getAttrOfType<ArrayAttr>(
"guards"))
4996 guards = guardsAttr.getValue();
4998 auto isCover = isa<CoverOp>(op);
4999 auto clock = getLoweredNonClockValue(opClock);
5000 auto enable = getLoweredValue(opEnable);
5001 auto predicate = getLoweredValue(opPredicate);
5002 if (!clock || !enable || !predicate)
5006 if (opNameAttr && !opNameAttr.getValue().empty())
5008 StringAttr prefixedLabel;
5011 StringAttr::get(builder.getContext(), labelPrefix + label.getValue());
5014 SmallVector<Value> messageOps;
5018 if (flavor == VerificationFlavor::IfElseFatal && !isa<AssertOp>(op))
5019 flavor = VerificationFlavor::None;
5021 if (flavor == VerificationFlavor::None) {
5025 auto format = op->getAttrOfType<StringAttr>(
"format");
5027 if (isConcurrent && format && format.getValue() ==
"ifElseFatal") {
5028 if (!isa<AssertOp>(op))
5029 return op->emitError()
5030 <<
"ifElseFatal format cannot be used for non-assertions";
5031 flavor = VerificationFlavor::IfElseFatal;
5032 }
else if (isConcurrent)
5033 flavor = VerificationFlavor::SVA;
5035 flavor = VerificationFlavor::Immediate;
5038 if (!isCover && opMessageAttr && !opMessageAttr.getValue().empty()) {
5039 message = opMessageAttr;
5040 if (failed(loweredFmtOperands(opOperands, messageOps)))
5043 if (flavor == VerificationFlavor::SVA) {
5048 for (
auto &loweredValue : messageOps)
5049 loweredValue = builder.create<
sv::SampledOp>(loweredValue);
5055 case VerificationFlavor::Immediate: {
5057 auto deferImmediate = circt::sv::DeferAssertAttr::get(
5058 builder.getContext(), circt::sv::DeferAssert::Immediate);
5059 addToAlwaysBlock(clock, [&]() {
5060 addIfProceduralBlock(enable, [&]() {
5062 prefixedLabel, message, messageOps);
5067 case VerificationFlavor::IfElseFatal: {
5068 assert(isa<AssertOp>(op) &&
"only assert is expected");
5071 auto boolType = IntegerType::get(builder.getContext(), 1);
5072 predicate = comb::createOrFoldNot(predicate, builder,
true);
5073 predicate = builder.createOrFold<
comb::AndOp>(enable, predicate,
true);
5075 circuitState.addMacroDecl(builder.getStringAttr(
"SYNTHESIS"));
5076 addToIfDefBlock(
"SYNTHESIS", {}, [&]() {
5077 addToAlwaysBlock(clock, [&]() {
5078 addIfProceduralBlock(predicate, [&]() {
5079 circuitState.usedStopCond =
true;
5080 circuitState.addFragment(theModule,
"STOP_COND_FRAGMENT");
5082 circuitState.usedAssertVerboseCond =
true;
5083 circuitState.addFragment(theModule,
"ASSERT_VERBOSE_COND_FRAGMENT");
5085 addIfProceduralBlock(
5086 builder.create<sv::MacroRefExprOp>(boolType,
5087 "ASSERT_VERBOSE_COND_"),
5088 [&]() { builder.create<sv::ErrorOp>(message, messageOps); });
5089 addIfProceduralBlock(
5090 builder.create<sv::MacroRefExprOp>(boolType,
"STOP_COND_"),
5091 [&]() { builder.create<sv::FatalOp>(); });
5097 case VerificationFlavor::SVA: {
5102 comb::createOrFoldNot(enable, builder,
true);
5104 builder.createOrFold<
comb::OrOp>(notEnable, predicate,
true);
5106 predicate = builder.createOrFold<
comb::AndOp>(enable, predicate,
true);
5110 sv::EventControl event;
5111 switch (opEventControl) {
5112 case EventControl::AtPosEdge:
5113 event = circt::sv::EventControl::AtPosEdge;
5115 case EventControl::AtEdge:
5116 event = circt::sv::EventControl::AtEdge;
5118 case EventControl::AtNegEdge:
5119 event = circt::sv::EventControl::AtNegEdge;
5125 circt::sv::EventControlAttr::get(builder.getContext(), event), clock,
5126 predicate, prefixedLabel, message, messageOps);
5129 case VerificationFlavor::None:
5131 "flavor `None` must be converted into one of concreate flavors");
5138 return emitGuards(op->getLoc(), guards,
emit);
5142LogicalResult FIRRTLLowering::visitStmt(AssertOp op) {
5143 return lowerVerificationStatement(
5144 op,
"assert__", op.getClock(), op.getPredicate(), op.getEnable(),
5145 op.getMessageAttr(), op.getSubstitutions(), op.getNameAttr(),
5146 op.getIsConcurrent(), op.getEventControl());
5150LogicalResult FIRRTLLowering::visitStmt(AssumeOp op) {
5151 return lowerVerificationStatement(
5152 op,
"assume__", op.getClock(), op.getPredicate(), op.getEnable(),
5153 op.getMessageAttr(), op.getSubstitutions(), op.getNameAttr(),
5154 op.getIsConcurrent(), op.getEventControl());
5158LogicalResult FIRRTLLowering::visitStmt(CoverOp op) {
5159 return lowerVerificationStatement(
5160 op,
"cover__", op.getClock(), op.getPredicate(), op.getEnable(),
5161 op.getMessageAttr(), op.getSubstitutions(), op.getNameAttr(),
5162 op.getIsConcurrent(), op.getEventControl());
5166LogicalResult FIRRTLLowering::visitStmt(UnclockedAssumeIntrinsicOp op) {
5171 auto guardsAttr = op->getAttrOfType<mlir::ArrayAttr>(
"guards");
5172 ArrayRef<Attribute> guards =
5173 guardsAttr ? guardsAttr.getValue() : ArrayRef<Attribute>();
5175 auto label = op.getNameAttr();
5176 StringAttr assumeLabel;
5177 if (label && !label.empty())
5179 StringAttr::get(builder.getContext(),
"assume__" + label.getValue());
5180 auto predicate = getLoweredValue(op.getPredicate());
5181 auto enable = getLoweredValue(op.getEnable());
5182 auto notEnable = comb::createOrFoldNot(enable, builder,
true);
5183 predicate = builder.createOrFold<
comb::OrOp>(notEnable, predicate,
true);
5185 SmallVector<Value> messageOps;
5186 for (
auto operand : op.getSubstitutions()) {
5187 auto loweredValue = getLoweredValue(operand);
5188 if (!loweredValue) {
5192 loweredValue = getOrCreateIntConstant(1, 0);
5194 messageOps.push_back(loweredValue);
5196 return emitGuards(op.getLoc(), guards, [&]() {
5197 builder.create<sv::AlwaysOp>(
5198 ArrayRef(sv::EventControl::AtEdge), ArrayRef(predicate), [&]() {
5199 if (op.getMessageAttr().getValue().empty())
5200 buildImmediateVerifOp(
5201 builder,
"assume", predicate,
5202 circt::sv::DeferAssertAttr::get(
5203 builder.getContext(), circt::sv::DeferAssert::Immediate),
5206 buildImmediateVerifOp(
5207 builder,
"assume", predicate,
5208 circt::sv::DeferAssertAttr::get(
5209 builder.getContext(), circt::sv::DeferAssert::Immediate),
5210 assumeLabel, op.getMessageAttr(), messageOps);
5215LogicalResult FIRRTLLowering::visitStmt(AttachOp op) {
5217 if (op.getAttached().size() < 2)
5220 SmallVector<Value, 4> inoutValues;
5221 for (
auto v : op.getAttached()) {
5222 inoutValues.push_back(getPossiblyInoutLoweredValue(v));
5223 if (!inoutValues.back()) {
5227 inoutValues.pop_back();
5231 if (!isa<hw::InOutType>(inoutValues.back().getType()))
5232 return op.emitError(
"operand isn't an inout type");
5235 if (inoutValues.size() < 2)
5246 bool isAttachInternalOnly =
5247 llvm::none_of(inoutValues, [](
auto v) {
return isa<BlockArgument>(v); });
5249 if (isAttachInternalOnly) {
5250 auto v0 = inoutValues.front();
5251 for (
auto v : inoutValues) {
5254 v.replaceAllUsesWith(v0);
5261 circuitState.addMacroDecl(builder.getStringAttr(
"SYNTHESIS"));
5262 circuitState.addMacroDecl(builder.getStringAttr(
"VERILATOR"));
5267 SmallVector<Value, 4> values;
5268 for (
auto inoutValue : inoutValues)
5269 values.push_back(getReadValue(inoutValue));
5271 for (
size_t i1 = 0, e = inoutValues.size(); i1 != e; ++i1) {
5272 for (
size_t i2 = 0; i2 != e; ++i2)
5274 builder.create<
sv::AssignOp>(inoutValues[i1], values[i2]);
5283 builder.create<sv::VerbatimOp>(
5284 "`error \"Verilator does not support alias and thus "
5286 "arbitrarily connect bidirectional wires and ports\"");
5288 [&]() { builder.create<sv::AliasOp>(inoutValues); });
5294LogicalResult FIRRTLLowering::visitStmt(BindOp op) {
5295 builder.create<sv::BindOp>(op.getInstanceAttr());
5299LogicalResult FIRRTLLowering::fixupLTLOps() {
5300 if (ltlOpFixupWorklist.empty())
5302 LLVM_DEBUG(llvm::dbgs() <<
"Fixing up " << ltlOpFixupWorklist.size()
5306 for (
unsigned i = 0, e = ltlOpFixupWorklist.size(); i != e; ++i)
5307 for (
auto *user : ltlOpFixupWorklist[i]->getUsers())
5308 if (isa<
hw::WireOp>(user))
5309 ltlOpFixupWorklist.insert(user);
5312 while (!ltlOpFixupWorklist.empty()) {
5313 auto *op = ltlOpFixupWorklist.pop_back_val();
5316 if (
auto opIntf = dyn_cast_or_null<mlir::InferTypeOpInterface>(op)) {
5317 LLVM_DEBUG(llvm::dbgs() <<
"- Update " << *op <<
"\n");
5318 SmallVector<Type, 2> types;
5319 auto result = opIntf.inferReturnTypes(
5320 op->getContext(), op->getLoc(), op->getOperands(),
5321 op->getAttrDictionary(), op->getPropertiesStorage(), op->getRegions(),
5325 assert(types.size() == op->getNumResults());
5329 for (
auto [result, type] :
llvm::zip(op->getResults(), types)) {
5330 if (result.getType() == type)
5332 LLVM_DEBUG(llvm::dbgs()
5333 <<
" - Result #" << result.getResultNumber() <<
" from "
5334 << result.getType() <<
" to " << type <<
"\n");
5335 result.setType(type);
5336 for (
auto *user : result.getUsers())
5338 ltlOpFixupWorklist.insert(user);
5343 if (
auto wireOp = dyn_cast<hw::WireOp>(op)) {
5344 if (isa<ltl::SequenceType, ltl::PropertyType>(wireOp.getType())) {
5345 wireOp.replaceAllUsesWith(wireOp.getInput());
5346 LLVM_DEBUG(llvm::dbgs() <<
"- Remove " << wireOp <<
"\n");
5347 if (wireOp.use_empty())
5354 SmallPtrSet<Operation *, 4> usersReported;
5355 for (
auto *user : op->getUsers()) {
5356 if (!usersReported.insert(user).second)
5358 if (isa<ltl::LTLDialect, verif::VerifDialect>(user->getDialect()))
5360 if (isa<hw::WireOp>(user))
5362 auto d = op->emitError(
5363 "verification operation used in a non-verification context");
5364 d.attachNote(user->getLoc())
5365 <<
"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