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_FD");
926 b.create<sv::MacroDeclOp>(
"PRINTF_FD_");
927 b.create<emit::FragmentOp>(
"PRINTF_FD_FRAGMENT", [&] {
928 b.create<sv::VerbatimOp>(
929 "\n// Users can define 'PRINTF_FD' to add a specified fd to "
931 emitGuard(
"PRINTF_FD_", [&]() {
932 emitGuardedDefine(
"PRINTF_FD",
"PRINTF_FD_",
"(`PRINTF_FD)",
937 b.create<sv::MacroDeclOp>(
"PRINTF_COND");
938 b.create<sv::MacroDeclOp>(
"PRINTF_COND_");
939 b.create<emit::FragmentOp>(
"PRINTF_COND_FRAGMENT", [&] {
940 b.create<sv::VerbatimOp>(
941 "\n// Users can define 'PRINTF_COND' to add an extra gate to "
943 emitGuard(
"PRINTF_COND_", [&]() {
944 emitGuardedDefine(
"PRINTF_COND",
"PRINTF_COND_",
"(`PRINTF_COND)",
"1");
949 if (state.usedAssertVerboseCond) {
950 b.create<sv::MacroDeclOp>(
"ASSERT_VERBOSE_COND");
951 b.create<sv::MacroDeclOp>(
"ASSERT_VERBOSE_COND_");
952 b.create<emit::FragmentOp>(
"ASSERT_VERBOSE_COND_FRAGMENT", [&] {
953 b.create<sv::VerbatimOp>(
954 "\n// Users can define 'ASSERT_VERBOSE_COND' to add an extra "
955 "gate to assert error printing.");
956 emitGuard(
"ASSERT_VERBOSE_COND_", [&]() {
957 emitGuardedDefine(
"ASSERT_VERBOSE_COND",
"ASSERT_VERBOSE_COND_",
958 "(`ASSERT_VERBOSE_COND)",
"1");
963 if (state.usedStopCond) {
964 b.create<sv::MacroDeclOp>(
"STOP_COND");
965 b.create<sv::MacroDeclOp>(
"STOP_COND_");
966 b.create<emit::FragmentOp>(
"STOP_COND_FRAGMENT", [&] {
967 b.create<sv::VerbatimOp>(
968 "\n// Users can define 'STOP_COND' to add an extra gate "
969 "to stop conditions.");
970 emitGuard(
"STOP_COND_", [&]() {
971 emitGuardedDefine(
"STOP_COND",
"STOP_COND_",
"(`STOP_COND)",
"1");
978FIRRTLModuleLowering::lowerPorts(ArrayRef<PortInfo> firrtlPorts,
979 SmallVectorImpl<hw::PortInfo> &ports,
980 Operation *moduleOp, StringRef moduleName,
982 ports.reserve(firrtlPorts.size());
984 size_t numResults = 0;
985 for (
auto e :
llvm::enumerate(firrtlPorts)) {
987 size_t portNo = e.index();
992 if (firrtlPort.
sym.size() > 1 ||
993 (firrtlPort.
sym.size() == 1 && !firrtlPort.
sym.getSymName()))
994 return emitError(firrtlPort.
loc)
995 <<
"cannot lower aggregate port " << firrtlPort.
name
996 <<
" with field sensitive symbols, HW dialect does not support "
997 "per field symbols yet.";
998 hwPort.
setSym(firrtlPort.
sym, moduleOp->getContext());
1000 if (hadDontTouch && !hwPort.
getSym()) {
1001 if (hwPort.
type.isInteger(0)) {
1002 if (enableAnnotationWarning) {
1003 mlir::emitWarning(firrtlPort.
loc)
1004 <<
"zero width port " << hwPort.
name
1005 <<
" has dontTouch annotation, removing anyway";
1011 hw::InnerSymAttr::get(StringAttr::get(
1012 moduleOp->getContext(),
1013 Twine(
"__") + moduleName + Twine(
"__DONTTOUCH__") +
1014 Twine(portNo) + Twine(
"__") + firrtlPort.
name.strref())),
1015 moduleOp->getContext());
1020 moduleOp->emitError(
"cannot lower this port type to HW");
1026 if (hwPort.
type.isInteger(0)) {
1027 auto sym = hwPort.
getSym();
1028 if (sym && !sym.empty()) {
1029 return mlir::emitError(firrtlPort.
loc)
1030 <<
"zero width port " << hwPort.
name
1031 <<
" is referenced by name [" << sym
1032 <<
"] (e.g. in an XMR) but must be removed";
1039 hwPort.
dir = hw::ModulePort::Direction::Output;
1040 hwPort.
argNum = numResults++;
1041 }
else if (firrtlPort.
isInput()) {
1042 hwPort.
dir = hw::ModulePort::Direction::Input;
1043 hwPort.
argNum = numArgs++;
1047 hwPort.
type = hw::InOutType::get(hwPort.
type);
1048 hwPort.
dir = hw::ModulePort::Direction::InOut;
1049 hwPort.
argNum = numArgs++;
1051 hwPort.
loc = firrtlPort.
loc;
1052 ports.push_back(hwPort);
1062 auto params = llvm::map_range(module.getParameters(), [](Attribute a) {
1063 return cast<ParamDeclAttr>(a);
1068 Builder builder(module);
1073 SmallVector<Attribute> newParams;
1074 for (
const ParamDeclAttr &entry : params) {
1075 auto name = entry.getName();
1076 auto type = entry.getType();
1077 auto value = ignoreValues ? Attribute() : entry.getValue();
1079 hw::ParamDeclAttr::get(builder.getContext(), name, type, value);
1080 newParams.push_back(paramAttr);
1082 return builder.getArrayAttr(newParams);
1085bool FIRRTLModuleLowering::handleForceNameAnnos(
1088 bool failed =
false;
1094 auto sym = anno.
getMember<FlatSymbolRefAttr>(
"circt.nonlocal");
1101 auto diag = oldModule.emitOpError()
1103 <<
"' that is not a non-local annotation";
1104 diag.attachNote() <<
"the erroneous annotation is '" << anno.
getDict()
1115 auto diag = oldModule.emitOpError()
1117 <<
"' whose non-local symbol, '" << sym
1118 <<
"' does not exist in the circuit";
1119 diag.attachNote() <<
"the erroneous annotation is '" << anno.
getDict();
1132 cast<hw::InnerRefAttr>(nla.getNamepath().getValue().take_back(2)[0]);
1134 {{inst.getModule(), inst.getName()}, anno.
getMember(
"name")});
1135 if (!inserted.second &&
1136 (anno.
getMember(
"name") != (inserted.first->second))) {
1137 auto diag = oldModule.emitError()
1139 <<
"' with different names: " << inserted.first->second
1140 <<
" was not " << anno.
getMember(
"name");
1141 diag.attachNote() <<
"the erroneous annotation is '" << anno.
getDict()
1152FIRRTLModuleLowering::lowerExtModule(FExtModuleOp oldModule,
1153 Block *topLevelModule,
1156 SmallVector<PortInfo> firrtlPorts = oldModule.getPorts();
1157 SmallVector<hw::PortInfo, 8> ports;
1158 if (failed(lowerPorts(firrtlPorts, ports, oldModule, oldModule.getName(),
1162 StringRef verilogName;
1163 if (
auto defName = oldModule.getDefname())
1164 verilogName = defName.value();
1167 auto builder = OpBuilder::atBlockEnd(topLevelModule);
1168 auto nameAttr = builder.getStringAttr(oldModule.getName());
1174 oldModule.getLoc(), nameAttr, ports, verilogName, parameters);
1175 SymbolTable::setSymbolVisibility(newModule,
1176 SymbolTable::getSymbolVisibility(oldModule));
1178 bool hasOutputPort =
1179 llvm::any_of(firrtlPorts, [&](
auto p) {
return p.isOutput(); });
1180 if (!hasOutputPort &&
1183 newModule->setAttr(
"firrtl.extract.cover.extra", builder.getUnitAttr());
1189 loweringState.processRemainingAnnotations(oldModule, annos);
1194FIRRTLModuleLowering::lowerMemModule(FMemModuleOp oldModule,
1195 Block *topLevelModule,
1198 SmallVector<PortInfo> firrtlPorts = oldModule.getPorts();
1199 SmallVector<hw::PortInfo, 8> ports;
1200 if (failed(lowerPorts(firrtlPorts, ports, oldModule, oldModule.getName(),
1205 auto builder = OpBuilder::atBlockEnd(topLevelModule);
1207 oldModule.getLoc(), oldModule.getModuleNameAttr(), ports,
1208 oldModule.getModuleNameAttr());
1216FIRRTLModuleLowering::lowerModule(FModuleOp oldModule, Block *topLevelModule,
1219 SmallVector<PortInfo> firrtlPorts = oldModule.getPorts();
1220 SmallVector<hw::PortInfo, 8> ports;
1221 if (failed(lowerPorts(firrtlPorts, ports, oldModule, oldModule.getName(),
1226 auto builder = OpBuilder::atBlockEnd(topLevelModule);
1227 auto nameAttr = builder.getStringAttr(oldModule.getName());
1229 builder.create<
hw::HWModuleOp>(oldModule.getLoc(), nameAttr, ports);
1231 if (
auto comment = oldModule->getAttrOfType<StringAttr>(
"comment"))
1232 newModule.setCommentAttr(comment);
1235 SmallVector<StringRef, 12> attrNames = {
1236 "annotations",
"convention",
"layers",
1237 "portNames",
"sym_name",
"portDirections",
1238 "portTypes",
"portAnnotations",
"portSymbols",
1239 "portLocations",
"parameters", SymbolTable::getVisibilityAttrName()};
1241 DenseSet<StringRef> attrSet(attrNames.begin(), attrNames.end());
1242 SmallVector<NamedAttribute> newAttrs(newModule->getAttrs());
1244 llvm::make_filter_range(oldModule->getAttrs(), [&](auto namedAttr) {
1245 return !attrSet.count(namedAttr.getName()) &&
1246 !newModule->getAttrDictionary().contains(namedAttr.getName());
1248 newAttrs.push_back(i);
1250 newModule->setAttrs(newAttrs);
1254 SymbolTable::setSymbolVisibility(newModule,
1255 SymbolTable::getSymbolVisibility(oldModule));
1261 newModule->setAttr(
"firrtl.extract.cover.extra", builder.getUnitAttr());
1265 if (
auto testBenchDir =
loweringState.getTestBenchDirectory())
1267 if (!newModule->hasAttr(
"output_file"))
1268 newModule->setAttr(
"output_file", testBenchDir);
1269 newModule->setAttr(
"firrtl.extract.do_not_extract",
1270 builder.getUnitAttr());
1271 newModule.setCommentAttr(
1272 builder.getStringAttr(
"VCS coverage exclude_file"));
1278 loweringState.processRemainingAnnotations(oldModule, annos);
1287 Operation *insertPoint) {
1288 if (!value.hasOneUse())
1291 auto attach = dyn_cast<AttachOp>(*value.user_begin());
1292 if (!attach || attach.getNumOperands() != 2)
1296 auto loweredType =
lowerType(value.getType());
1297 if (loweredType.isInteger(0))
1302 auto attachedValue = attach.getOperand(attach.getOperand(0) == value);
1303 auto *op = attachedValue.getDefiningOp();
1304 if (op && op->getBlock() == insertPoint->getBlock() &&
1305 !op->isBeforeInBlock(insertPoint))
1310 ImplicitLocOpBuilder builder(insertPoint->getLoc(), insertPoint);
1326 if (type_isa<AnalogType>(flipValue.getType()))
1329 Operation *connectOp =
nullptr;
1330 for (
auto &use : flipValue.getUses()) {
1333 if (use.getOperandNumber() != 0)
1335 if (!isa<ConnectOp, MatchingConnectOp>(use.getOwner()))
1341 connectOp = use.getOwner();
1351 loweringState.lowerType(flipValue.getType(), flipValue.getLoc());
1352 if (loweredType.isInteger(0))
1357 ImplicitLocOpBuilder builder(insertPoint->getLoc(), insertPoint);
1359 auto connectSrc = connectOp->getOperand(1);
1362 if (!isa<FIRRTLType>(connectSrc.getType())) {
1368 if (!type_cast<FIRRTLBaseType>(connectSrc.getType()).isPassive())
1369 connectSrc = builder
1370 .create<mlir::UnrealizedConversionCastOp>(
1371 type_cast<FIRRTLBaseType>(connectSrc.getType())
1378 auto destTy = type_cast<FIRRTLBaseType>(flipValue.getType()).getPassiveType();
1380 if (destTy != connectSrc.getType() &&
1381 (isa<BaseTypeAliasType>(connectSrc.getType()) ||
1382 isa<BaseTypeAliasType>(destTy))) {
1384 builder.createOrFold<BitCastOp>(flipValue.getType(), connectSrc);
1386 if (!destTy.isGround()) {
1388 if (destTy != type_cast<FIRRTLType>(connectSrc.getType()))
1390 }
else if (destTy.getBitWidthOrSentinel() !=
1391 type_cast<FIRRTLBaseType>(connectSrc.getType())
1392 .getBitWidthOrSentinel()) {
1395 auto destWidth = destTy.getBitWidthOrSentinel();
1396 assert(destWidth != -1 &&
"must know integer widths");
1397 connectSrc = builder.createOrFold<PadPrimOp>(destTy, connectSrc, destWidth);
1409 SmallVector<SubfieldOp> accesses;
1410 for (
auto *op : structValue.getUsers()) {
1411 assert(isa<SubfieldOp>(op));
1412 auto fieldAccess = cast<SubfieldOp>(op);
1414 fieldAccess.getInput().getType().base().getElementIndex(field);
1415 if (elemIndex && *elemIndex == fieldAccess.getFieldIndex())
1416 accesses.push_back(fieldAccess);
1424LogicalResult FIRRTLModuleLowering::lowerModulePortsAndMoveBody(
1427 ImplicitLocOpBuilder bodyBuilder(oldModule.getLoc(), newModule.getBody());
1434 bodyBuilder.setInsertionPoint(cursor);
1437 SmallVector<PortInfo> firrtlPorts = oldModule.getPorts();
1438 assert(oldModule.getBody().getNumArguments() == firrtlPorts.size() &&
1439 "port count mismatch");
1441 SmallVector<Value, 4> outputs;
1444 auto *outputOp = newModule.getBodyBlock()->getTerminator();
1445 ImplicitLocOpBuilder outputBuilder(oldModule.getLoc(), outputOp);
1447 unsigned nextHWInputArg = 0;
1448 int hwPortIndex = -1;
1449 for (
auto [firrtlPortIndex, port] :
llvm::enumerate(firrtlPorts)) {
1451 auto oldArg = oldModule.getBody().getArgument(firrtlPortIndex);
1454 type_isa<FIRRTLBaseType>(port.type) &&
1455 type_cast<FIRRTLBaseType>(port.type).getBitWidthOrSentinel() == 0;
1459 if (!port.isOutput() && !isZeroWidth) {
1462 Value newArg = newModule.getBody().getArgument(nextHWInputArg++);
1468 oldArg.replaceAllUsesWith(newArg);
1474 if (isZeroWidth && port.isInput()) {
1475 Value newArg = bodyBuilder
1476 .create<WireOp>(port.type,
"." + port.getName().str() +
1479 oldArg.replaceAllUsesWith(newArg);
1487 outputs.push_back(value);
1488 assert(oldArg.use_empty() &&
"should have removed all uses of oldArg");
1494 auto newArg = bodyBuilder.create<WireOp>(
1495 port.type,
"." + port.getName().str() +
".output");
1498 oldArg.replaceAllUsesWith(newArg.getResult());
1501 auto resultHWType =
loweringState.lowerType(port.type, port.loc);
1502 if (!resultHWType.isInteger(0)) {
1505 outputs.push_back(output);
1508 if (
auto sym = newModule.getPort(hwPortIndex).getSym()) {
1509 newArg.setInnerSymAttr(sym);
1510 newModule.setPortSymbolAttr(hwPortIndex, {});
1516 outputOp->setOperands(outputs);
1519 auto &oldBlockInstList = oldModule.getBodyBlock()->getOperations();
1520 auto &newBlockInstList = newModule.getBodyBlock()->getOperations();
1521 newBlockInstList.splice(Block::iterator(cursor), oldBlockInstList,
1522 oldBlockInstList.begin(), oldBlockInstList.end());
1533FIRRTLModuleLowering::lowerFormalBody(verif::FormalOp newOp,
1535 auto builder = OpBuilder::atBlockEnd(&newOp.getBody().front());
1540 auto oldOp = cast<FormalOp>(
loweringState.getOldModule(newOp));
1541 auto moduleName = oldOp.getModuleNameAttr().getAttr();
1542 auto oldModule = cast<FModuleOp>(
1543 loweringState.getInstanceGraph().lookup(moduleName)->getModule());
1544 auto newModule = cast<hw::HWModuleOp>(
loweringState.getNewModule(oldModule));
1547 SmallVector<Value> symbolicInputs;
1548 for (
auto arg : newModule.getBody().getArguments())
1549 symbolicInputs.push_back(
1550 builder.create<
verif::SymbolicValueOp>(arg.
getLoc(), arg.getType()));
1553 builder.create<hw::InstanceOp>(newOp.getLoc(), newModule,
1554 newModule.getNameAttr(), symbolicInputs);
1561FIRRTLModuleLowering::lowerSimulationBody(verif::SimulationOp newOp,
1563 auto builder = OpBuilder::atBlockEnd(newOp.getBody());
1566 auto oldOp = cast<SimulationOp>(
loweringState.getOldModule(newOp));
1567 auto moduleName = oldOp.getModuleNameAttr().getAttr();
1568 auto oldModule = cast<FModuleLike>(
1569 *
loweringState.getInstanceGraph().lookup(moduleName)->getModule());
1571 cast<hw::HWModuleLike>(
loweringState.getNewModule(oldModule));
1575 SmallVector<Value> inputs(newOp.getBody()->args_begin(),
1576 newOp.getBody()->args_end());
1577 auto instOp = builder.create<hw::InstanceOp>(newOp.getLoc(), newModule,
1578 newModule.getNameAttr(), inputs);
1579 builder.create<verif::YieldOp>(newOp.getLoc(), instOp.getResults());
1589struct FIRRTLLowering :
public FIRRTLVisitor<FIRRTLLowering, LogicalResult> {
1591 FIRRTLLowering(
hw::HWModuleOp module, CircuitLoweringState &circuitState)
1592 : theModule(module), circuitState(circuitState),
1593 builder(module.
getLoc(), module.getContext()), moduleNamespace(module),
1594 backedgeBuilder(builder, module.
getLoc()) {}
1596 LogicalResult
run();
1599 Value getOrCreateClockConstant(seq::ClockConst clock);
1600 Value getOrCreateIntConstant(
const APInt &value);
1601 Value getOrCreateIntConstant(
unsigned numBits, uint64_t val,
1602 bool isSigned =
false) {
1603 return getOrCreateIntConstant(APInt(numBits, val, isSigned));
1605 Attribute getOrCreateAggregateConstantAttribute(Attribute value, Type type);
1606 Value getOrCreateXConstant(
unsigned numBits);
1607 Value getOrCreateZConstant(Type type);
1608 Value getPossiblyInoutLoweredValue(Value value);
1609 Value getLoweredValue(Value value);
1610 Value getLoweredNonClockValue(Value value);
1611 Value getLoweredAndExtendedValue(Value value, Type destType);
1612 Value getLoweredAndExtOrTruncValue(Value value, Type destType);
1613 LogicalResult setLowering(Value orig, Value result);
1614 LogicalResult setPossiblyFoldedLowering(Value orig, Value result);
1615 template <
typename ResultOpType,
typename... CtorArgTypes>
1616 LogicalResult setLoweringTo(Operation *orig, CtorArgTypes... args);
1617 template <
typename ResultOpType,
typename... CtorArgTypes>
1618 LogicalResult setLoweringToLTL(Operation *orig, CtorArgTypes... args);
1619 Backedge createBackedge(Location loc, Type type);
1620 Backedge createBackedge(Value orig, Type type);
1621 bool updateIfBackedge(Value dest, Value src);
1624 bool requiresInnerSymbol(hw::InnerSymbolOpInterface op) {
1629 if (
auto forceable = dyn_cast<Forceable>(op.getOperation()))
1630 if (forceable.isForceable())
1638 hw::InnerSymAttr lowerInnerSymbol(hw::InnerSymbolOpInterface op) {
1639 auto attr = op.getInnerSymAttr();
1643 if (requiresInnerSymbol(op))
1645 op.getContext(), attr, 0,
1650 void runWithInsertionPointAtEndOfBlock(
const std::function<
void(
void)> &fn,
1654 Value getReadValue(Value v);
1656 Value getNonClockValue(Value v);
1658 void addToAlwaysBlock(sv::EventControl clockEdge, Value clock,
1659 sv::ResetType resetStyle, sv::EventControl resetEdge,
1660 Value reset,
const std::function<
void(
void)> &body = {},
1661 const std::function<void(
void)> &resetBody = {});
1662 void addToAlwaysBlock(Value clock,
1663 const std::function<
void(
void)> &body = {}) {
1664 addToAlwaysBlock(sv::EventControl::AtPosEdge, clock, sv::ResetType(),
1665 sv::EventControl(), Value(), body,
1666 std::function<
void(
void)>());
1669 LogicalResult emitGuards(Location loc, ArrayRef<Attribute> guards,
1670 std::function<
void(
void)>
emit);
1671 void addToIfDefBlock(StringRef cond, std::function<
void(
void)> thenCtor,
1672 std::function<
void(
void)> elseCtor = {});
1673 void addToInitialBlock(std::function<
void(
void)> body);
1674 void addIfProceduralBlock(Value cond, std::function<
void(
void)> thenCtor,
1675 std::function<
void(
void)> elseCtor = {});
1676 Value getExtOrTruncAggregateValue(Value array,
FIRRTLBaseType sourceType,
1678 bool allowTruncate);
1679 Value createArrayIndexing(Value array, Value index);
1680 Value createValueWithMuxAnnotation(Operation *op,
bool isMux2);
1682 using FIRRTLVisitor<FIRRTLLowering, LogicalResult>::visitExpr;
1683 using FIRRTLVisitor<FIRRTLLowering, LogicalResult>::visitDecl;
1684 using FIRRTLVisitor<FIRRTLLowering, LogicalResult>::visitStmt;
1687 enum UnloweredOpResult { AlreadyLowered, NowLowered, LoweringFailure };
1688 UnloweredOpResult handleUnloweredOp(Operation *op);
1689 LogicalResult visitExpr(ConstantOp op);
1690 LogicalResult visitExpr(SpecialConstantOp op);
1691 LogicalResult visitExpr(SubindexOp op);
1692 LogicalResult visitExpr(SubaccessOp op);
1693 LogicalResult visitExpr(SubfieldOp op);
1694 LogicalResult visitExpr(VectorCreateOp op);
1695 LogicalResult visitExpr(BundleCreateOp op);
1696 LogicalResult visitExpr(FEnumCreateOp op);
1697 LogicalResult visitExpr(AggregateConstantOp op);
1698 LogicalResult visitExpr(IsTagOp op);
1699 LogicalResult visitExpr(SubtagOp op);
1702 if (
auto castOp = dyn_cast<mlir::UnrealizedConversionCastOp>(op))
1703 return visitUnrealizedConversionCast(castOp);
1708 LogicalResult visitDecl(WireOp op);
1709 LogicalResult visitDecl(NodeOp op);
1710 LogicalResult visitDecl(RegOp op);
1711 LogicalResult visitDecl(RegResetOp op);
1712 LogicalResult visitDecl(MemOp op);
1713 LogicalResult visitDecl(InstanceOp oldInstance);
1714 LogicalResult visitDecl(VerbatimWireOp op);
1715 LogicalResult visitDecl(ContractOp op);
1718 LogicalResult lowerNoopCast(Operation *op);
1719 LogicalResult visitExpr(AsSIntPrimOp op);
1720 LogicalResult visitExpr(AsUIntPrimOp op);
1721 LogicalResult visitExpr(AsClockPrimOp op);
1722 LogicalResult visitExpr(AsAsyncResetPrimOp op) {
return lowerNoopCast(op); }
1724 LogicalResult visitExpr(HWStructCastOp op);
1725 LogicalResult visitExpr(BitCastOp op);
1727 visitUnrealizedConversionCast(mlir::UnrealizedConversionCastOp op);
1728 LogicalResult visitExpr(CvtPrimOp op);
1729 LogicalResult visitExpr(NotPrimOp op);
1730 LogicalResult visitExpr(NegPrimOp op);
1731 LogicalResult visitExpr(PadPrimOp op);
1732 LogicalResult visitExpr(XorRPrimOp op);
1733 LogicalResult visitExpr(AndRPrimOp op);
1734 LogicalResult visitExpr(OrRPrimOp op);
1737 template <
typename ResultUnsignedOpType,
1738 typename ResultSignedOpType = ResultUnsignedOpType>
1739 LogicalResult lowerBinOp(Operation *op);
1740 template <
typename ResultOpType>
1741 LogicalResult lowerBinOpToVariadic(Operation *op);
1743 template <
typename ResultOpType>
1744 LogicalResult lowerElementwiseLogicalOp(Operation *op);
1746 LogicalResult lowerCmpOp(Operation *op, ICmpPredicate signedOp,
1747 ICmpPredicate unsignedOp);
1748 template <
typename SignedOp,
typename Un
signedOp>
1749 LogicalResult lowerDivLikeOp(Operation *op);
1751 LogicalResult visitExpr(CatPrimOp op);
1753 LogicalResult visitExpr(AndPrimOp op) {
1754 return lowerBinOpToVariadic<comb::AndOp>(op);
1756 LogicalResult visitExpr(OrPrimOp op) {
1757 return lowerBinOpToVariadic<comb::OrOp>(op);
1759 LogicalResult visitExpr(XorPrimOp op) {
1760 return lowerBinOpToVariadic<comb::XorOp>(op);
1762 LogicalResult visitExpr(ElementwiseOrPrimOp op) {
1763 return lowerElementwiseLogicalOp<comb::OrOp>(op);
1765 LogicalResult visitExpr(ElementwiseAndPrimOp op) {
1766 return lowerElementwiseLogicalOp<comb::AndOp>(op);
1768 LogicalResult visitExpr(ElementwiseXorPrimOp op) {
1769 return lowerElementwiseLogicalOp<comb::XorOp>(op);
1771 LogicalResult visitExpr(AddPrimOp op) {
1772 return lowerBinOpToVariadic<comb::AddOp>(op);
1774 LogicalResult visitExpr(EQPrimOp op) {
1775 return lowerCmpOp(op, ICmpPredicate::eq, ICmpPredicate::eq);
1777 LogicalResult visitExpr(NEQPrimOp op) {
1778 return lowerCmpOp(op, ICmpPredicate::ne, ICmpPredicate::ne);
1780 LogicalResult visitExpr(LTPrimOp op) {
1781 return lowerCmpOp(op, ICmpPredicate::slt, ICmpPredicate::ult);
1783 LogicalResult visitExpr(LEQPrimOp op) {
1784 return lowerCmpOp(op, ICmpPredicate::sle, ICmpPredicate::ule);
1786 LogicalResult visitExpr(GTPrimOp op) {
1787 return lowerCmpOp(op, ICmpPredicate::sgt, ICmpPredicate::ugt);
1789 LogicalResult visitExpr(GEQPrimOp op) {
1790 return lowerCmpOp(op, ICmpPredicate::sge, ICmpPredicate::uge);
1793 LogicalResult visitExpr(SubPrimOp op) {
return lowerBinOp<comb::SubOp>(op); }
1794 LogicalResult visitExpr(MulPrimOp op) {
1795 return lowerBinOpToVariadic<comb::MulOp>(op);
1797 LogicalResult visitExpr(DivPrimOp op) {
1798 return lowerDivLikeOp<comb::DivSOp, comb::DivUOp>(op);
1800 LogicalResult visitExpr(RemPrimOp op) {
1801 return lowerDivLikeOp<comb::ModSOp, comb::ModUOp>(op);
1805 LogicalResult visitExpr(IsXIntrinsicOp op);
1806 LogicalResult visitExpr(PlusArgsTestIntrinsicOp op);
1807 LogicalResult visitExpr(PlusArgsValueIntrinsicOp op);
1808 LogicalResult visitStmt(FPGAProbeIntrinsicOp op);
1809 LogicalResult visitExpr(ClockInverterIntrinsicOp op);
1810 LogicalResult visitExpr(ClockDividerIntrinsicOp op);
1811 LogicalResult visitExpr(SizeOfIntrinsicOp op);
1812 LogicalResult visitExpr(ClockGateIntrinsicOp op);
1813 LogicalResult visitExpr(LTLAndIntrinsicOp op);
1814 LogicalResult visitExpr(LTLOrIntrinsicOp op);
1815 LogicalResult visitExpr(LTLIntersectIntrinsicOp op);
1816 LogicalResult visitExpr(LTLDelayIntrinsicOp op);
1817 LogicalResult visitExpr(LTLConcatIntrinsicOp op);
1818 LogicalResult visitExpr(LTLRepeatIntrinsicOp op);
1819 LogicalResult visitExpr(LTLGoToRepeatIntrinsicOp op);
1820 LogicalResult visitExpr(LTLNonConsecutiveRepeatIntrinsicOp op);
1821 LogicalResult visitExpr(LTLNotIntrinsicOp op);
1822 LogicalResult visitExpr(LTLImplicationIntrinsicOp op);
1823 LogicalResult visitExpr(LTLUntilIntrinsicOp op);
1824 LogicalResult visitExpr(LTLEventuallyIntrinsicOp op);
1825 LogicalResult visitExpr(LTLClockIntrinsicOp op);
1827 template <
typename TargetOp,
typename IntrinsicOp>
1828 LogicalResult lowerVerifIntrinsicOp(IntrinsicOp op);
1829 LogicalResult visitStmt(VerifAssertIntrinsicOp op);
1830 LogicalResult visitStmt(VerifAssumeIntrinsicOp op);
1831 LogicalResult visitStmt(VerifCoverIntrinsicOp op);
1832 LogicalResult visitStmt(VerifRequireIntrinsicOp op);
1833 LogicalResult visitStmt(VerifEnsureIntrinsicOp op);
1834 LogicalResult visitExpr(HasBeenResetIntrinsicOp op);
1835 LogicalResult visitStmt(UnclockedAssumeIntrinsicOp op);
1838 LogicalResult visitExpr(BitsPrimOp op);
1839 LogicalResult visitExpr(InvalidValueOp op);
1840 LogicalResult visitExpr(HeadPrimOp op);
1841 LogicalResult visitExpr(ShlPrimOp op);
1842 LogicalResult visitExpr(ShrPrimOp op);
1843 LogicalResult visitExpr(DShlPrimOp op) {
1844 return lowerDivLikeOp<comb::ShlOp, comb::ShlOp>(op);
1846 LogicalResult visitExpr(DShrPrimOp op) {
1847 return lowerDivLikeOp<comb::ShrSOp, comb::ShrUOp>(op);
1849 LogicalResult visitExpr(DShlwPrimOp op) {
1850 return lowerDivLikeOp<comb::ShlOp, comb::ShlOp>(op);
1852 LogicalResult visitExpr(TailPrimOp op);
1853 LogicalResult visitExpr(MuxPrimOp op);
1854 LogicalResult visitExpr(Mux2CellIntrinsicOp op);
1855 LogicalResult visitExpr(Mux4CellIntrinsicOp op);
1856 LogicalResult visitExpr(MultibitMuxOp op);
1857 LogicalResult visitExpr(VerbatimExprOp op);
1858 LogicalResult visitExpr(XMRRefOp op);
1859 LogicalResult visitExpr(XMRDerefOp op);
1862 LogicalResult visitExpr(TimeOp op);
1863 LogicalResult visitExpr(HierarchicalModuleNameOp op);
1866 LogicalResult lowerVerificationStatement(
1867 Operation *op, StringRef labelPrefix, Value clock, Value predicate,
1868 Value enable, StringAttr messageAttr, ValueRange operands,
1869 StringAttr nameAttr,
bool isConcurrent, EventControl eventControl);
1871 LogicalResult visitStmt(SkipOp op);
1873 FailureOr<bool> lowerConnect(Value dest, Value srcVal);
1874 LogicalResult visitStmt(ConnectOp op);
1875 LogicalResult visitStmt(MatchingConnectOp op);
1876 LogicalResult visitStmt(ForceOp op);
1878 std::optional<Value> getLoweredFmtOperand(Value operand);
1879 LogicalResult loweredFmtOperands(ValueRange operands,
1880 SmallVectorImpl<Value> &loweredOperands);
1881 FailureOr<Value> callFileDescriptorLib(
const FileDescriptorInfo &info);
1885 LogicalResult lowerStatementWithFd(
1886 const FileDescriptorInfo &fileDescriptorInfo, Value clock, Value cond,
1887 const std::function<LogicalResult(Value)> &fn,
bool usePrintfCond);
1891 LogicalResult visitPrintfLike(T op,
1892 const FileDescriptorInfo &fileDescriptorInfo,
1893 bool usePrintfCond);
1894 LogicalResult visitStmt(PrintFOp op) {
return visitPrintfLike(op, {},
true); }
1895 LogicalResult visitStmt(FPrintFOp op);
1896 LogicalResult visitStmt(FFlushOp op);
1897 LogicalResult visitStmt(StopOp op);
1898 LogicalResult visitStmt(AssertOp op);
1899 LogicalResult visitStmt(AssumeOp op);
1900 LogicalResult visitStmt(CoverOp op);
1901 LogicalResult visitStmt(AttachOp op);
1902 LogicalResult visitStmt(RefForceOp op);
1903 LogicalResult visitStmt(RefForceInitialOp op);
1904 LogicalResult visitStmt(RefReleaseOp op);
1905 LogicalResult visitStmt(RefReleaseInitialOp op);
1906 LogicalResult visitStmt(BindOp op);
1908 FailureOr<Value> lowerSubindex(SubindexOp op, Value input);
1909 FailureOr<Value> lowerSubaccess(SubaccessOp op, Value input);
1910 FailureOr<Value> lowerSubfield(SubfieldOp op, Value input);
1912 LogicalResult fixupLTLOps();
1915 return circuitState.lowerType(type, builder.getLoc());
1923 CircuitLoweringState &circuitState;
1926 ImplicitLocOpBuilder builder;
1931 DenseMap<Value, Value> valueMapping;
1935 DenseMap<Value, Value> fromClockMapping;
1939 DenseMap<Attribute, Value> hwConstantMap;
1940 DenseMap<std::pair<Attribute, Type>, Attribute> hwAggregateConstantMap;
1944 DenseMap<unsigned, Value> hwConstantXMap;
1945 DenseMap<Type, Value> hwConstantZMap;
1951 DenseMap<Value, Value> readInOutCreated;
1954 DenseMap<StringAttr, sv::RegOp> fileNameToFileDescriptor;
1958 using AlwaysKeyType = std::tuple<
Block *, sv::EventControl, Value,
1959 sv::ResetType, sv::EventControl, Value>;
1976 llvm::MapVector<Value, Value> backedges;
1983 DenseSet<Operation *> maybeUnusedValues;
1985 void maybeUnused(Operation *op) { maybeUnusedValues.insert(op); }
1986 void maybeUnused(Value value) {
1987 if (
auto *op = value.getDefiningOp())
1999 SetVector<Operation *> ltlOpFixupWorklist;
2004 SmallVector<std::pair<Block::iterator, Block::iterator>> worklist;
2006 void addToWorklist(Block &block) {
2007 worklist.push_back({block.begin(), block.end()});
2009 void addToWorklist(Region ®ion) {
2010 for (
auto &block :
llvm::reverse(region))
2011 addToWorklist(block);
2022LogicalResult FIRRTLModuleLowering::lowerFileBody(emit::FileOp fileOp) {
2023 OpBuilder b(&getContext());
2024 fileOp->walk([&](Operation *op) {
2025 if (
auto bindOp = dyn_cast<BindOp>(op)) {
2026 b.setInsertionPointAfter(bindOp);
2027 b.create<sv::BindOp>(bindOp.getLoc(), bindOp.getInstanceAttr());
2035FIRRTLModuleLowering::lowerBody(Operation *op,
2037 if (
auto moduleOp = dyn_cast<hw::HWModuleOp>(op))
2039 if (
auto formalOp = dyn_cast<verif::FormalOp>(op))
2041 if (
auto simulationOp = dyn_cast<verif::SimulationOp>(op))
2043 if (
auto fileOp = dyn_cast<emit::FileOp>(op))
2044 return lowerFileBody(fileOp);
2049LogicalResult FIRRTLLowering::run() {
2052 for (
auto arg : theModule.
getBodyBlock()->getArguments())
2053 if (failed(setLowering(arg, arg)))
2060 addToWorklist(theModule.getBody());
2061 SmallVector<Operation *, 16> opsToRemove;
2063 while (!worklist.empty()) {
2064 auto &[opsIt, opsEnd] = worklist.back();
2065 if (opsIt == opsEnd) {
2066 worklist.pop_back();
2069 Operation *op = &*opsIt++;
2071 builder.setInsertionPoint(op);
2072 builder.setLoc(op->getLoc());
2073 auto done = succeeded(dispatchVisitor(op));
2074 circuitState.processRemainingAnnotations(op,
AnnotationSet(op));
2076 opsToRemove.push_back(op);
2078 switch (handleUnloweredOp(op)) {
2079 case AlreadyLowered:
2082 opsToRemove.push_back(op);
2084 case LoweringFailure:
2096 for (
auto &[backedge, value] : backedges) {
2097 SmallVector<Location> driverLocs;
2103 if (backedge == value) {
2104 Location edgeLoc = backedge.getLoc();
2105 if (driverLocs.empty()) {
2106 mlir::emitError(edgeLoc,
"sink does not have a driver");
2108 auto diag = mlir::emitError(edgeLoc,
"sink in combinational loop");
2109 for (
auto loc : driverLocs)
2110 diag.attachNote(loc) <<
"through driver here";
2116 auto *it = backedges.find(value);
2117 if (it == backedges.end())
2120 driverLocs.push_back(value.getLoc());
2123 if (
auto *defOp = backedge.getDefiningOp())
2124 maybeUnusedValues.erase(defOp);
2125 backedge.replaceAllUsesWith(value);
2133 while (!opsToRemove.empty()) {
2134 auto *op = opsToRemove.pop_back_val();
2139 for (
auto result : op->getResults()) {
2143 auto builder = OpBuilder::atBlockBegin(theModule.getBodyBlock());
2145 builder.getIntegerType(0), 0);
2146 maybeUnusedValues.insert(zeroI0);
2148 result.replaceAllUsesWith(zeroI0);
2151 if (!op->use_empty()) {
2152 auto d = op->emitOpError(
2153 "still has uses; should remove ops in reverse order of visitation");
2154 SmallPtrSet<Operation *, 2> visited;
2155 for (
auto *user : op->getUsers())
2156 if (visited.insert(user).second)
2157 d.attachNote(user->
getLoc())
2158 <<
"used by " << user->
getName() <<
" op";
2161 maybeUnusedValues.erase(op);
2166 while (!maybeUnusedValues.empty()) {
2167 auto it = maybeUnusedValues.begin();
2169 maybeUnusedValues.erase(it);
2170 if (!isOpTriviallyDead(op))
2172 for (
auto operand : op->getOperands())
2173 if (auto *defOp = operand.getDefiningOp())
2174 maybeUnusedValues.insert(defOp);
2180 if (failed(fixupLTLOps()))
2191Value FIRRTLLowering::getOrCreateClockConstant(seq::ClockConst clock) {
2192 auto attr = seq::ClockConstAttr::get(theModule.getContext(), clock);
2194 auto &entry = hwConstantMap[attr];
2198 OpBuilder entryBuilder(&theModule.getBodyBlock()->front());
2199 entry = entryBuilder.create<seq::ConstClockOp>(builder.getLoc(), attr);
2205Value FIRRTLLowering::getOrCreateIntConstant(
const APInt &value) {
2206 auto attr = builder.getIntegerAttr(
2207 builder.getIntegerType(value.getBitWidth()), value);
2209 auto &entry = hwConstantMap[attr];
2213 OpBuilder entryBuilder(&theModule.getBodyBlock()->front());
2214 entry = entryBuilder.create<
hw::ConstantOp>(builder.getLoc(), attr);
2220Attribute FIRRTLLowering::getOrCreateAggregateConstantAttribute(Attribute value,
2223 if (hw::type_isa<IntegerType>(type))
2224 return builder.getIntegerAttr(type, cast<IntegerAttr>(value).getValue());
2226 auto cache = hwAggregateConstantMap.lookup({value, type});
2231 SmallVector<Attribute> values;
2232 for (
auto e :
llvm::enumerate(cast<ArrayAttr>(value))) {
2234 if (
auto array = hw::type_dyn_cast<hw::ArrayType>(type))
2235 subType = array.getElementType();
2236 else if (
auto structType = hw::type_dyn_cast<hw::StructType>(type))
2237 subType = structType.getElements()[e.index()].type;
2239 assert(
false &&
"type must be either array or struct");
2241 values.push_back(getOrCreateAggregateConstantAttribute(e.value(), subType));
2245 if (hw::type_isa<hw::ArrayType>(type))
2246 std::reverse(values.begin(), values.end());
2248 auto &entry = hwAggregateConstantMap[{value, type}];
2249 entry = builder.getArrayAttr(values);
2257 const std::function<LogicalResult()> &fn) {
2258 assert(failedOperand &&
"Should be called on the failed operand");
2266Value FIRRTLLowering::getOrCreateXConstant(
unsigned numBits) {
2268 auto &entry = hwConstantXMap[numBits];
2272 OpBuilder entryBuilder(&theModule.getBodyBlock()->front());
2273 entry = entryBuilder.create<sv::ConstantXOp>(
2274 builder.getLoc(), entryBuilder.getIntegerType(numBits));
2278Value FIRRTLLowering::getOrCreateZConstant(Type type) {
2279 auto &entry = hwConstantZMap[type];
2281 OpBuilder entryBuilder(&theModule.getBodyBlock()->front());
2282 entry = entryBuilder.create<sv::ConstantZOp>(builder.getLoc(), type);
2291Value FIRRTLLowering::getPossiblyInoutLoweredValue(Value value) {
2293 if (
auto lowering = valueMapping.lookup(value)) {
2294 assert(!isa<FIRRTLType>(lowering.getType()) &&
2295 "Lowered value should be a non-FIRRTL value");
2304Value FIRRTLLowering::getLoweredValue(Value value) {
2305 auto result = getPossiblyInoutLoweredValue(value);
2311 if (isa<hw::InOutType>(result.getType()))
2312 return getReadValue(result);
2318Value FIRRTLLowering::getLoweredNonClockValue(Value value) {
2319 auto result = getLoweredValue(value);
2323 if (hw::type_isa<seq::ClockType>(result.getType()))
2324 return getNonClockValue(result);
2332Value FIRRTLLowering::getExtOrTruncAggregateValue(Value array,
2335 bool allowTruncate) {
2336 SmallVector<Value> resultBuffer;
2341 auto srcWidth = firrtl::type_cast<IntType>(sourceType).getWidthOrSentinel();
2342 auto destWidth = firrtl::type_cast<IntType>(destType).getWidthOrSentinel();
2343 auto resultType = builder.getIntegerType(destWidth);
2345 if (srcWidth == destWidth)
2348 if (srcWidth > destWidth) {
2352 builder.emitError(
"operand should not be a truncation");
2356 if (firrtl::type_cast<IntType>(sourceType).isSigned())
2357 return comb::createOrFoldSExt(value, resultType, builder);
2358 auto zero = getOrCreateIntConstant(destWidth - srcWidth, 0);
2366 return TypeSwitch<FIRRTLBaseType, LogicalResult>(srcType)
2367 .Case<FVectorType>([&](
auto srcVectorType) {
2368 auto destVectorType = firrtl::type_cast<FVectorType>(destType);
2369 unsigned size = resultBuffer.size();
2370 unsigned indexWidth =
2372 for (
size_t i = 0, e = std::min(srcVectorType.getNumElements(),
2373 destVectorType.getNumElements());
2375 auto iIdx = getOrCreateIntConstant(indexWidth, i);
2377 if (failed(recurse(arrayIndex, srcVectorType.getElementType(),
2378 destVectorType.getElementType())))
2381 SmallVector<Value> temp(resultBuffer.begin() + size,
2382 resultBuffer.end());
2384 resultBuffer.resize(size);
2385 resultBuffer.push_back(array);
2388 .Case<BundleType>([&](BundleType srcStructType) {
2389 auto destStructType = firrtl::type_cast<BundleType>(destType);
2390 unsigned size = resultBuffer.size();
2393 if (destStructType.getNumElements() != srcStructType.getNumElements())
2396 for (
auto elem :
llvm::enumerate(destStructType)) {
2397 auto structExtract =
2399 if (failed(recurse(structExtract,
2400 srcStructType.getElementType(elem.index()),
2401 destStructType.getElementType(elem.index()))))
2404 SmallVector<Value> temp(resultBuffer.begin() + size,
2405 resultBuffer.end());
2408 resultBuffer.resize(size);
2409 resultBuffer.push_back(newStruct);
2412 .Case<IntType>([&](
auto) {
2413 if (
auto result = cast(src, srcType, destType)) {
2414 resultBuffer.push_back(result);
2419 .Default([&](
auto) {
return failure(); });
2422 if (failed(recurse(array, sourceType, destType)))
2425 assert(resultBuffer.size() == 1 &&
2426 "resultBuffer must only contain a result array if `success` is true");
2427 return resultBuffer[0];
2435Value FIRRTLLowering::getLoweredAndExtendedValue(Value value, Type destType) {
2436 assert(type_isa<FIRRTLBaseType>(value.getType()) &&
2437 type_isa<FIRRTLBaseType>(destType) &&
2438 "input/output value should be FIRRTL");
2441 auto destWidth = type_cast<FIRRTLBaseType>(destType).getBitWidthOrSentinel();
2442 if (destWidth == -1)
2445 auto result = getLoweredValue(value);
2457 return getOrCreateIntConstant(destWidth, 0);
2461 cast<FIRRTLBaseType>(value.getType()).getBitWidthOrSentinel()) {
2463 auto loweredDstType =
lowerType(destType);
2464 if (result.getType() != loweredDstType &&
2465 (isa<hw::TypeAliasType>(result.getType()) ||
2466 isa<hw::TypeAliasType>(loweredDstType))) {
2467 return builder.createOrFold<
hw::BitcastOp>(loweredDstType, result);
2471 if (isa<hw::ArrayType, hw::StructType>(result.getType())) {
2473 if (destType == value.getType())
2476 return getExtOrTruncAggregateValue(
2477 result, type_cast<FIRRTLBaseType>(value.getType()),
2478 type_cast<FIRRTLBaseType>(destType),
2482 if (isa<seq::ClockType>(result.getType())) {
2484 if (destType == value.getType())
2486 builder.emitError(
"cannot use clock type as an integer");
2490 auto intResultType = dyn_cast<IntegerType>(result.getType());
2491 if (!intResultType) {
2492 builder.emitError(
"operand of type ")
2493 << result.getType() <<
" cannot be used as an integer";
2497 auto srcWidth = intResultType.getWidth();
2498 if (srcWidth ==
unsigned(destWidth))
2501 if (srcWidth >
unsigned(destWidth)) {
2502 builder.emitError(
"operand should not be a truncation");
2506 auto resultType = builder.getIntegerType(destWidth);
2510 type_cast<FIRRTLBaseType>(value.getType()).getPassiveType();
2511 if (type_cast<IntType>(valueFIRType).isSigned())
2512 return comb::createOrFoldSExt(result, resultType, builder);
2514 auto zero = getOrCreateIntConstant(destWidth - srcWidth, 0);
2523Value FIRRTLLowering::getLoweredAndExtOrTruncValue(Value value, Type destType) {
2524 assert(type_isa<FIRRTLBaseType>(value.getType()) &&
2525 type_isa<FIRRTLBaseType>(destType) &&
2526 "input/output value should be FIRRTL");
2529 auto destWidth = type_cast<FIRRTLBaseType>(destType).getBitWidthOrSentinel();
2530 if (destWidth == -1)
2533 auto result = getLoweredValue(value);
2545 return getOrCreateIntConstant(destWidth, 0);
2549 if (isa<hw::ArrayType, hw::StructType>(result.getType())) {
2551 if (destType == value.getType())
2554 return getExtOrTruncAggregateValue(
2555 result, type_cast<FIRRTLBaseType>(value.getType()),
2556 type_cast<FIRRTLBaseType>(destType),
2560 auto srcWidth = type_cast<IntegerType>(result.getType()).getWidth();
2561 if (srcWidth ==
unsigned(destWidth))
2567 if (srcWidth >
unsigned(destWidth)) {
2568 auto resultType = builder.getIntegerType(destWidth);
2572 auto resultType = builder.getIntegerType(destWidth);
2576 type_cast<FIRRTLBaseType>(value.getType()).getPassiveType();
2577 if (type_cast<IntType>(valueFIRType).isSigned())
2578 return comb::createOrFoldSExt(result, resultType, builder);
2580 auto zero = getOrCreateIntConstant(destWidth - srcWidth, 0);
2594std::optional<Value> FIRRTLLowering::getLoweredFmtOperand(Value operand) {
2596 if (type_isa<FStringType>(operand.getType())) {
2597 if (isa<TimeOp>(operand.getDefiningOp()))
2598 return builder.create<sv::TimeOp>();
2599 if (isa<HierarchicalModuleNameOp>(operand.getDefiningOp()))
2603 auto loweredValue = getLoweredValue(operand);
2604 if (!loweredValue) {
2608 loweredValue = getOrCreateIntConstant(1, 0);
2613 if (
auto intTy = firrtl::type_cast<IntType>(operand.getType()))
2614 if (intTy.isSigned())
2615 loweredValue = builder.create<sv::SystemFunctionOp>(
2616 loweredValue.getType(),
"signed", loweredValue);
2618 return loweredValue;
2622FIRRTLLowering::loweredFmtOperands(mlir::ValueRange operands,
2623 SmallVectorImpl<Value> &loweredOperands) {
2624 for (
auto operand : operands) {
2625 std::optional<Value> loweredValue = getLoweredFmtOperand(operand);
2630 loweredOperands.push_back(*loweredValue);
2635LogicalResult FIRRTLLowering::lowerStatementWithFd(
2636 const FileDescriptorInfo &fileDescriptor, Value clock, Value cond,
2637 const std::function<LogicalResult(Value)> &fn,
bool usePrintfCond) {
2639 bool failed =
false;
2640 circuitState.addMacroDecl(builder.getStringAttr(
"SYNTHESIS"));
2641 addToIfDefBlock(
"SYNTHESIS", std::function<
void()>(), [&]() {
2642 addToAlwaysBlock(clock, [&]() {
2645 circuitState.usedPrintf =
true;
2647 circuitState.addFragment(theModule,
"PRINTF_COND_FRAGMENT");
2650 Value ifCond = cond;
2651 if (usePrintfCond) {
2653 builder.create<sv::MacroRefExprOp>(cond.getType(),
"PRINTF_COND_");
2654 ifCond = builder.createOrFold<
comb::AndOp>(ifCond, cond,
true);
2657 addIfProceduralBlock(ifCond, [&]() {
2661 if (fileDescriptor.isDefaultFd()) {
2662 fd = builder.create<sv::MacroRefExprOp>(builder.getIntegerType(32),
2664 circuitState.addFragment(theModule,
"PRINTF_FD_FRAGMENT");
2667 auto fdOrError = callFileDescriptorLib(fileDescriptor);
2668 if (llvm::failed(fdOrError)) {
2674 failed = llvm::failed(fn(fd));
2678 return failure(failed);
2682FIRRTLLowering::callFileDescriptorLib(
const FileDescriptorInfo &info) {
2683 circuitState.usedFileDescriptorLib =
true;
2684 circuitState.addFragment(theModule,
"CIRCT_LIB_LOGGING_FRAGMENT");
2687 if (
info.isSubstitutionRequired()) {
2688 SmallVector<Value> fileNameOperands;
2689 if (failed(loweredFmtOperands(
info.getSubstitutions(), fileNameOperands)))
2693 .create<sv::SFormatFOp>(
info.getOutputFileFormat(),
2698 fileName = builder.create<sv::ConstantStrOp>(
info.getOutputFileFormat())
2703 .create<sv::FuncCallProceduralOp>(
2704 mlir::TypeRange{builder.getIntegerType(32)},
2705 builder.getStringAttr(
"__circt_lib_logging::FileDescriptor::get"),
2706 ValueRange{fileName})
2716LogicalResult FIRRTLLowering::setLowering(Value orig, Value result) {
2717 if (
auto origType = dyn_cast<FIRRTLType>(orig.getType())) {
2718 assert((!result || !type_isa<FIRRTLType>(result.getType())) &&
2719 "Lowering didn't turn a FIRRTL value into a non-FIRRTL value");
2723 auto srcWidth = baseType.getPassiveType().getBitWidthOrSentinel();
2726 if (srcWidth != -1) {
2728 assert((srcWidth != 0) &&
2729 "Lowering produced value for zero width source");
2731 assert((srcWidth == 0) &&
2732 "Lowering produced null value but source wasn't zero width");
2736 assert(result &&
"Lowering of foreign type produced null value");
2739 auto &slot = valueMapping[orig];
2740 assert(!slot &&
"value lowered multiple times");
2747LogicalResult FIRRTLLowering::setPossiblyFoldedLowering(Value orig,
2751 if (
auto cst = dyn_cast_or_null<hw::ConstantOp>(result.getDefiningOp())) {
2752 auto &entry = hwConstantMap[cst.getValueAttr()];
2763 cst->moveBefore(&theModule.getBodyBlock()->front());
2767 return setLowering(orig, result);
2772template <
typename ResultOpType,
typename... CtorArgTypes>
2773LogicalResult FIRRTLLowering::setLoweringTo(Operation *orig,
2774 CtorArgTypes... args) {
2775 auto result = builder.createOrFold<ResultOpType>(args...);
2776 if (
auto *op = result.getDefiningOp())
2778 return setPossiblyFoldedLowering(orig->getResult(0), result);
2785template <
typename ResultOpType,
typename... CtorArgTypes>
2786LogicalResult FIRRTLLowering::setLoweringToLTL(Operation *orig,
2787 CtorArgTypes... args) {
2788 auto result = builder.createOrFold<ResultOpType>(args...);
2789 if (
auto *op = result.getDefiningOp())
2790 ltlOpFixupWorklist.insert(op);
2791 return setPossiblyFoldedLowering(orig->getResult(0), result);
2800Backedge FIRRTLLowering::createBackedge(Location loc, Type type) {
2801 auto backedge = backedgeBuilder.
get(type, loc);
2802 backedges.insert({backedge, backedge});
2810Backedge FIRRTLLowering::createBackedge(Value orig, Type type) {
2811 auto backedge = createBackedge(orig.getLoc(), type);
2812 (void)setLowering(orig, backedge);
2818bool FIRRTLLowering::updateIfBackedge(Value dest, Value src) {
2819 auto backedgeIt = backedges.find(dest);
2820 if (backedgeIt == backedges.end())
2822 backedgeIt->second = src;
2830void FIRRTLLowering::runWithInsertionPointAtEndOfBlock(
2831 const std::function<
void(
void)> &fn, Region ®ion) {
2835 auto oldIP = builder.saveInsertionPoint();
2837 builder.setInsertionPointToEnd(®ion.front());
2839 builder.restoreInsertionPoint(oldIP);
2843Value FIRRTLLowering::getReadValue(Value v) {
2844 Value result = readInOutCreated.lookup(v);
2850 auto oldIP = builder.saveInsertionPoint();
2851 if (
auto *vOp = v.getDefiningOp()) {
2852 builder.setInsertionPointAfter(vOp);
2856 builder.setInsertionPoint(&theModule.getBodyBlock()->front());
2861 if (
auto arrayIndexInout = v.getDefiningOp<sv::ArrayIndexInOutOp>()) {
2862 result = getReadValue(arrayIndexInout.getInput());
2864 arrayIndexInout.getIndex());
2869 builder.restoreInsertionPoint(oldIP);
2870 readInOutCreated.insert({v, result});
2874Value FIRRTLLowering::getNonClockValue(Value v) {
2875 auto it = fromClockMapping.try_emplace(v, Value{});
2877 ImplicitLocOpBuilder builder(v.getLoc(), v.getContext());
2878 builder.setInsertionPointAfterValue(v);
2879 it.first->second = builder.create<seq::FromClockOp>(v);
2881 return it.first->second;
2884void FIRRTLLowering::addToAlwaysBlock(
2885 sv::EventControl clockEdge, Value clock, sv::ResetType resetStyle,
2886 sv::EventControl resetEdge, Value reset,
2887 const std::function<
void(
void)> &body,
2888 const std::function<
void(
void)> &resetBody) {
2889 AlwaysKeyType key{builder.getBlock(), clockEdge, clock,
2890 resetStyle, resetEdge, reset};
2891 sv::AlwaysOp alwaysOp;
2892 sv::IfOp insideIfOp;
2893 std::tie(alwaysOp, insideIfOp) = alwaysBlocks.lookup(key);
2897 assert(resetStyle != sv::ResetType::NoReset);
2910 auto createIfOp = [&]() {
2913 insideIfOp = builder.create<sv::IfOp>(
2914 reset, []() {}, []() {});
2916 if (resetStyle == sv::ResetType::AsyncReset) {
2917 sv::EventControl events[] = {clockEdge, resetEdge};
2918 Value clocks[] = {clock, reset};
2920 alwaysOp = builder.create<sv::AlwaysOp>(events, clocks, [&]() {
2921 if (resetEdge == sv::EventControl::AtNegEdge)
2922 llvm_unreachable(
"negative edge for reset is not expected");
2926 alwaysOp = builder.create<sv::AlwaysOp>(clockEdge, clock, createIfOp);
2930 alwaysOp = builder.create<sv::AlwaysOp>(clockEdge, clock);
2931 insideIfOp =
nullptr;
2933 alwaysBlocks[key] = {alwaysOp, insideIfOp};
2937 assert(insideIfOp &&
"reset body must be initialized before");
2938 runWithInsertionPointAtEndOfBlock(resetBody, insideIfOp.getThenRegion());
2939 runWithInsertionPointAtEndOfBlock(body, insideIfOp.getElseRegion());
2941 runWithInsertionPointAtEndOfBlock(body, alwaysOp.getBody());
2947 alwaysOp->moveBefore(builder.getInsertionBlock(),
2948 builder.getInsertionPoint());
2951LogicalResult FIRRTLLowering::emitGuards(Location loc,
2952 ArrayRef<Attribute> guards,
2953 std::function<
void(
void)>
emit) {
2954 if (guards.empty()) {
2958 auto guard = dyn_cast<StringAttr>(guards[0]);
2960 return mlir::emitError(loc,
2961 "elements in `guards` array must be `StringAttr`");
2964 circuitState.addMacroDecl(builder.getStringAttr(guard.getValue()));
2965 LogicalResult result = LogicalResult::failure();
2966 addToIfDefBlock(guard.getValue(), [&]() {
2967 result = emitGuards(loc, guards.drop_front(), emit);
2972void FIRRTLLowering::addToIfDefBlock(StringRef cond,
2973 std::function<
void(
void)> thenCtor,
2974 std::function<
void(
void)> elseCtor) {
2975 auto condAttr = builder.getStringAttr(cond);
2976 auto op = ifdefBlocks.lookup({builder.getBlock(), condAttr});
2978 runWithInsertionPointAtEndOfBlock(thenCtor, op.getThenRegion());
2979 runWithInsertionPointAtEndOfBlock(elseCtor, op.getElseRegion());
2984 op->moveBefore(builder.getInsertionBlock(), builder.getInsertionPoint());
2986 ifdefBlocks[{builder.getBlock(), condAttr}] =
2987 builder.create<
sv::IfDefOp>(condAttr, thenCtor, elseCtor);
2991void FIRRTLLowering::addToInitialBlock(std::function<
void(
void)> body) {
2992 auto op = initialBlocks.lookup(builder.getBlock());
2994 runWithInsertionPointAtEndOfBlock(body, op.getBody());
2999 op->moveBefore(builder.getInsertionBlock(), builder.getInsertionPoint());
3001 initialBlocks[builder.getBlock()] = builder.create<sv::InitialOp>(body);
3005void FIRRTLLowering::addIfProceduralBlock(Value cond,
3006 std::function<
void(
void)> thenCtor,
3007 std::function<
void(
void)> elseCtor) {
3010 auto insertIt = builder.getInsertionPoint();
3011 if (insertIt != builder.getBlock()->begin())
3012 if (
auto ifOp = dyn_cast<sv::IfOp>(*--insertIt)) {
3013 if (ifOp.getCond() == cond) {
3014 runWithInsertionPointAtEndOfBlock(thenCtor, ifOp.getThenRegion());
3015 runWithInsertionPointAtEndOfBlock(elseCtor, ifOp.getElseRegion());
3020 builder.create<sv::IfOp>(cond, thenCtor, elseCtor);
3032FIRRTLLowering::UnloweredOpResult
3033FIRRTLLowering::handleUnloweredOp(Operation *op) {
3035 if (!op->getRegions().empty() && isa<FIRRTLDialect>(op->getDialect())) {
3036 op->emitOpError(
"must explicitly handle its regions");
3037 return LoweringFailure;
3044 if (!isa<FIRRTLDialect>(op->getDialect())) {
3046 for (
auto ®ion : op->getRegions())
3047 addToWorklist(region);
3048 for (
auto &operand : op->getOpOperands())
3049 if (auto lowered = getPossiblyInoutLoweredValue(operand.
get()))
3050 operand.set(lowered);
3051 for (
auto result : op->getResults())
3052 (void)setLowering(result, result);
3053 return AlreadyLowered;
3065 if (op->getNumResults() == 1) {
3066 auto resultType = op->getResult(0).getType();
3067 if (type_isa<FIRRTLBaseType>(resultType) &&
3069 (
isExpression(op) || isa<mlir::UnrealizedConversionCastOp>(op))) {
3071 (void)setLowering(op->getResult(0), Value());
3075 op->emitOpError(
"LowerToHW couldn't handle this operation");
3076 return LoweringFailure;
3079LogicalResult FIRRTLLowering::visitExpr(ConstantOp op) {
3082 return setLowering(op, Value());
3084 return setLowering(op, getOrCreateIntConstant(op.getValue()));
3087LogicalResult FIRRTLLowering::visitExpr(SpecialConstantOp op) {
3089 if (isa<ClockType>(op.getType())) {
3090 cst = getOrCreateClockConstant(op.getValue() ? seq::ClockConst::High
3091 :
seq::ClockConst::Low);
3093 cst = getOrCreateIntConstant(APInt( 1, op.getValue()));
3095 return setLowering(op, cst);
3098FailureOr<Value> FIRRTLLowering::lowerSubindex(SubindexOp op, Value input) {
3099 auto iIdx = getOrCreateIntConstant(
3101 firrtl::type_cast<FVectorType>(op.getInput().getType())
3108 if (isa<sv::InOutType>(input.getType()))
3109 result = builder.createOrFold<sv::ArrayIndexInOutOp>(input, iIdx);
3112 if (
auto *definingOp = result.getDefiningOp())
3117FailureOr<Value> FIRRTLLowering::lowerSubaccess(SubaccessOp op, Value input) {
3118 Value valueIdx = getLoweredAndExtOrTruncValue(
3120 UIntType::get(op->getContext(),
3122 firrtl::type_cast<FVectorType>(op.getInput().getType())
3123 .getNumElements())));
3125 op->emitError() <<
"input lowering failed";
3132 if (isa<sv::InOutType>(input.getType()))
3133 result = builder.createOrFold<sv::ArrayIndexInOutOp>(input, valueIdx);
3135 result = createArrayIndexing(input, valueIdx);
3136 if (
auto *definingOp = result.getDefiningOp())
3141FailureOr<Value> FIRRTLLowering::lowerSubfield(SubfieldOp op, Value input) {
3142 auto resultType =
lowerType(op->getResult(0).getType());
3143 if (!resultType || !input) {
3144 op->emitError() <<
"subfield type lowering failed";
3150 auto field = firrtl::type_cast<BundleType>(op.getInput().getType())
3151 .getElementName(op.getFieldIndex());
3153 if (isa<sv::InOutType>(input.getType()))
3154 result = builder.createOrFold<sv::StructFieldInOutOp>(input, field);
3157 if (
auto *definingOp = result.getDefiningOp())
3162LogicalResult FIRRTLLowering::visitExpr(SubindexOp op) {
3164 return setLowering(op, Value());
3166 auto input = getPossiblyInoutLoweredValue(op.getInput());
3168 return op.emitError() <<
"input lowering failed";
3170 auto result = lowerSubindex(op, input);
3173 return setLowering(op, *result);
3176LogicalResult FIRRTLLowering::visitExpr(SubaccessOp op) {
3178 return setLowering(op, Value());
3180 auto input = getPossiblyInoutLoweredValue(op.getInput());
3182 return op.emitError() <<
"input lowering failed";
3184 auto result = lowerSubaccess(op, input);
3187 return setLowering(op, *result);
3190LogicalResult FIRRTLLowering::visitExpr(SubfieldOp op) {
3193 if (getLoweredValue(op) || !op.getInput())
3197 return setLowering(op, Value());
3199 auto input = getPossiblyInoutLoweredValue(op.getInput());
3201 return op.emitError() <<
"input lowering failed";
3203 auto result = lowerSubfield(op, input);
3206 return setLowering(op, *result);
3209LogicalResult FIRRTLLowering::visitExpr(VectorCreateOp op) {
3210 auto resultType =
lowerType(op.getResult().getType());
3211 SmallVector<Value> operands;
3213 for (
auto oper :
llvm::reverse(op.getOperands())) {
3214 auto val = getLoweredValue(oper);
3217 operands.push_back(val);
3219 return setLoweringTo<hw::ArrayCreateOp>(op, resultType, operands);
3222LogicalResult FIRRTLLowering::visitExpr(BundleCreateOp op) {
3223 auto resultType =
lowerType(op.getResult().getType());
3224 SmallVector<Value> operands;
3225 for (
auto oper : op.getOperands()) {
3226 auto val = getLoweredValue(oper);
3229 operands.push_back(val);
3231 return setLoweringTo<hw::StructCreateOp>(op, resultType, operands);
3234LogicalResult FIRRTLLowering::visitExpr(FEnumCreateOp op) {
3237 return setLowering(op, Value());
3239 auto input = getLoweredValue(op.getInput());
3240 auto tagName = op.getFieldNameAttr();
3243 if (
auto structType = dyn_cast<hw::StructType>(type)) {
3244 auto enumType = structType.getFieldType(
"tag");
3245 auto enumAttr = hw::EnumFieldAttr::get(op.getLoc(), tagName, enumType);
3246 auto enumOp = builder.create<hw::EnumConstantOp>(enumAttr);
3247 auto unionType = structType.getFieldType(
"body");
3248 auto unionOp = builder.create<hw::UnionCreateOp>(unionType, tagName, input);
3249 SmallVector<Value> operands = {enumOp.getResult(), unionOp.getResult()};
3250 return setLoweringTo<hw::StructCreateOp>(op, structType, operands);
3253 return setLoweringTo<hw::EnumConstantOp>(
3254 op, hw::EnumFieldAttr::get(op.getLoc(), tagName, type));
3257LogicalResult FIRRTLLowering::visitExpr(AggregateConstantOp op) {
3258 auto resultType =
lowerType(op.getResult().getType());
3260 getOrCreateAggregateConstantAttribute(op.getFieldsAttr(), resultType);
3262 return setLoweringTo<hw::AggregateConstantOp>(op, resultType,
3263 cast<ArrayAttr>(attr));
3266LogicalResult FIRRTLLowering::visitExpr(IsTagOp op) {
3267 auto tagName = op.getFieldNameAttr();
3268 auto lhs = getLoweredValue(op.getInput());
3269 if (isa<hw::StructType>(lhs.getType()))
3271 auto enumField = hw::EnumFieldAttr::get(op.getLoc(), tagName, lhs.getType());
3272 auto rhs = builder.create<hw::EnumConstantOp>(enumField);
3273 return setLoweringTo<hw::EnumCmpOp>(op, lhs, rhs);
3276LogicalResult FIRRTLLowering::visitExpr(SubtagOp op) {
3279 return setLowering(op, Value());
3281 auto tagName = op.getFieldNameAttr();
3282 auto input = getLoweredValue(op.getInput());
3284 return setLoweringTo<hw::UnionExtractOp>(op, field, tagName);
3291LogicalResult FIRRTLLowering::visitDecl(WireOp op) {
3292 auto origResultType = op.getResult().getType();
3296 if (!type_isa<FIRRTLType>(origResultType)) {
3297 createBackedge(op.getResult(), origResultType);
3301 auto resultType =
lowerType(origResultType);
3305 if (resultType.isInteger(0)) {
3306 if (op.getInnerSym())
3307 return op.emitError(
"zero width wire is referenced by name [")
3308 << *op.getInnerSym() <<
"] (e.g. in an XMR) but must be removed";
3309 return setLowering(op.getResult(), Value());
3313 auto innerSym = lowerInnerSymbol(op);
3314 auto name = op.getNameAttr();
3317 auto wire = builder.create<hw::WireOp>(
3318 op.getLoc(), getOrCreateZConstant(resultType), name, innerSym);
3320 if (
auto svAttrs = sv::getSVAttributes(op))
3321 sv::setSVAttributes(wire, svAttrs);
3323 return setLowering(op.getResult(), wire);
3326LogicalResult FIRRTLLowering::visitDecl(VerbatimWireOp op) {
3327 auto resultTy =
lowerType(op.getType());
3330 resultTy = sv::InOutType::get(op.getContext(), resultTy);
3332 SmallVector<Value, 4> operands;
3333 operands.reserve(op.getSubstitutions().size());
3334 for (
auto operand : op.getSubstitutions()) {
3335 auto lowered = getLoweredValue(operand);
3338 operands.push_back(lowered);
3341 ArrayAttr symbols = op.getSymbolsAttr();
3343 symbols = ArrayAttr::get(op.getContext(), {});
3345 return setLoweringTo<sv::VerbatimExprSEOp>(op, resultTy, op.getTextAttr(),
3349LogicalResult FIRRTLLowering::visitDecl(NodeOp op) {
3350 auto operand = getLoweredValue(op.getInput());
3352 return handleZeroBit(op.getInput(), [&]() -> LogicalResult {
3353 if (op.getInnerSym())
3354 return op.emitError(
"zero width node is referenced by name [")
3355 << *op.getInnerSym()
3356 <<
"] (e.g. in an XMR) but must be "
3358 return setLowering(op.getResult(), Value());
3364 auto name = op.getNameAttr();
3365 auto innerSym = lowerInnerSymbol(op);
3368 operand = builder.create<hw::WireOp>(operand, name, innerSym);
3371 if (
auto svAttrs = sv::getSVAttributes(op)) {
3373 operand = builder.create<hw::WireOp>(operand, name);
3374 sv::setSVAttributes(operand.getDefiningOp(), svAttrs);
3377 return setLowering(op.getResult(), operand);
3380LogicalResult FIRRTLLowering::visitDecl(RegOp op) {
3381 auto resultType =
lowerType(op.getResult().getType());
3384 if (resultType.isInteger(0))
3385 return setLowering(op.getResult(), Value());
3387 Value clockVal = getLoweredValue(op.getClockVal());
3392 auto innerSym = lowerInnerSymbol(op);
3393 Backedge inputEdge = backedgeBuilder.
get(resultType);
3394 auto reg = builder.create<seq::FirRegOp>(inputEdge, clockVal,
3395 op.getNameAttr(), innerSym);
3398 if (
auto randomRegister = op->getAttr(
"firrtl.random_init_register"))
3399 reg->setAttr(
"firrtl.random_init_register", randomRegister);
3400 if (
auto randomStart = op->getAttr(
"firrtl.random_init_start"))
3401 reg->setAttr(
"firrtl.random_init_start", randomStart);
3402 if (
auto randomEnd = op->getAttr(
"firrtl.random_init_end"))
3403 reg->setAttr(
"firrtl.random_init_end", randomEnd);
3406 if (
auto svAttrs = sv::getSVAttributes(op))
3407 sv::setSVAttributes(reg, svAttrs);
3409 inputEdge.setValue(reg);
3410 (void)setLowering(op.getResult(),
reg);
3414LogicalResult FIRRTLLowering::visitDecl(RegResetOp op) {
3415 auto resultType =
lowerType(op.getResult().getType());
3418 if (resultType.isInteger(0))
3419 return setLowering(op.getResult(), Value());
3421 Value clockVal = getLoweredValue(op.getClockVal());
3422 Value resetSignal = getLoweredValue(op.getResetSignal());
3424 Value resetValue = getLoweredAndExtOrTruncValue(
3425 op.getResetValue(), type_cast<FIRRTLBaseType>(op.getResult().getType()));
3427 if (!clockVal || !resetSignal || !resetValue)
3431 auto innerSym = lowerInnerSymbol(op);
3432 bool isAsync = type_isa<AsyncResetType>(op.getResetSignal().getType());
3433 Backedge inputEdge = backedgeBuilder.
get(resultType);
3435 builder.create<seq::FirRegOp>(inputEdge, clockVal, op.getNameAttr(),
3436 resetSignal, resetValue, innerSym, isAsync);
3439 if (
auto randomRegister = op->getAttr(
"firrtl.random_init_register"))
3440 reg->setAttr(
"firrtl.random_init_register", randomRegister);
3441 if (
auto randomStart = op->getAttr(
"firrtl.random_init_start"))
3442 reg->setAttr(
"firrtl.random_init_start", randomStart);
3443 if (
auto randomEnd = op->getAttr(
"firrtl.random_init_end"))
3444 reg->setAttr(
"firrtl.random_init_end", randomEnd);
3447 if (
auto svAttrs = sv::getSVAttributes(op))
3448 sv::setSVAttributes(reg, svAttrs);
3450 inputEdge.setValue(reg);
3451 (void)setLowering(op.getResult(),
reg);
3456LogicalResult FIRRTLLowering::visitDecl(MemOp op) {
3459 if (type_isa<BundleType>(op.getDataType()))
3460 return op.emitOpError(
3461 "should have already been lowered from a ground type to an aggregate "
3462 "type using the LowerTypes pass. Use "
3463 "'firtool --lower-types' or 'circt-opt "
3464 "--pass-pipeline='firrtl.circuit(firrtl-lower-types)' "
3470 auto memType = seq::FirMemType::get(
3473 : std::optional<uint32_t>());
3475 seq::FirMemInitAttr memInit;
3476 if (
auto init = op.getInitAttr())
3477 memInit = seq::FirMemInitAttr::get(init.getContext(), init.getFilename(),
3478 init.getIsBinary(), init.getIsInline());
3480 auto memDecl = builder.create<seq::FirMemOp>(
3483 op.getInnerSymAttr(), memInit, op.getPrefixAttr(), Attribute{});
3487 if (!circuitState.isInDUT(theModule))
3488 if (
auto testBenchDir = circuitState.getTestBenchDirectory())
3489 memDecl.setOutputFileAttr(testBenchDir);
3493 for (
size_t i = 0, e = op.getNumResults(); i != e; ++i) {
3495 auto addOutput = [&](StringRef field,
size_t width, Value value) {
3498 (void)setLowering(a, value);
3504 auto addInput = [&](StringRef field, Value backedge) {
3506 if (cast<FIRRTLBaseType>(a.getType())
3508 .getBitWidthOrSentinel() > 0)
3509 (void)setLowering(a, backedge);
3515 auto addInputPort = [&](StringRef field,
size_t width) -> Value {
3519 Value backedge, portValue;
3521 portValue = getOrCreateXConstant(1);
3523 auto portType = IntegerType::get(op.getContext(), width);
3524 backedge = portValue = createBackedge(builder.getLoc(), portType);
3526 addInput(field, backedge);
3530 auto addClock = [&](StringRef field) -> Value {
3531 Type clockTy = seq::ClockType::get(op.getContext());
3532 Value portValue = createBackedge(builder.getLoc(), clockTy);
3533 addInput(field, portValue);
3537 auto memportKind = op.getPortKind(i);
3538 if (memportKind == MemOp::PortKind::Read) {
3539 auto addr = addInputPort(
"addr", op.getAddrBits());
3540 auto en = addInputPort(
"en", 1);
3541 auto clk = addClock(
"clk");
3542 auto data = builder.create<seq::FirMemReadOp>(memDecl,
addr,
clk,
en);
3544 }
else if (memportKind == MemOp::PortKind::ReadWrite) {
3545 auto addr = addInputPort(
"addr", op.getAddrBits());
3546 auto en = addInputPort(
"en", 1);
3547 auto clk = addClock(
"clk");
3550 auto mode = addInputPort(
"wmode", 1);
3552 mode = builder.createOrFold<
comb::AndOp>(mode, addInputPort(
"wmask", 1),
3559 auto rdata = builder.create<seq::FirMemReadWriteOp>(
3563 auto addr = addInputPort(
"addr", op.getAddrBits());
3566 auto en = addInputPort(
"en", 1);
3570 auto clk = addClock(
"clk");
3583LogicalResult FIRRTLLowering::visitDecl(InstanceOp oldInstance) {
3584 Operation *oldModule =
3585 oldInstance.getReferencedModule(circuitState.getInstanceGraph());
3587 auto *newModule = circuitState.getNewModule(oldModule);
3589 oldInstance->emitOpError(
"could not find module [")
3590 << oldInstance.getModuleName() <<
"] referenced by instance";
3596 ArrayAttr parameters;
3597 if (
auto oldExtModule = dyn_cast<FExtModuleOp>(oldModule))
3602 SmallVector<PortInfo, 8> portInfo = cast<FModuleLike>(oldModule).getPorts();
3607 for (
unsigned portIdx = 0, e = portInfo.size(); portIdx != e; ++portIdx)
3608 portIndicesByName[portInfo[portIdx].name] = portIdx;
3612 SmallVector<Value, 8> operands;
3613 for (
size_t portIndex = 0, e = portInfo.size(); portIndex != e; ++portIndex) {
3614 auto &port = portInfo[portIndex];
3617 oldInstance->emitOpError(
"could not lower type of port ") << port.name;
3622 if (portType.isInteger(0))
3626 if (port.isOutput())
3629 auto portResult = oldInstance.getResult(portIndex);
3630 assert(portResult &&
"invalid IR, couldn't find port");
3634 if (port.isInput()) {
3635 operands.push_back(createBackedge(portResult, portType));
3641 if (type_isa<AnalogType>(portResult.getType()) && portResult.hasOneUse()) {
3642 if (
auto attach = dyn_cast<AttachOp>(*portResult.getUsers().begin())) {
3644 auto loweredResult = getPossiblyInoutLoweredValue(source);
3645 operands.push_back(loweredResult);
3646 (void)setLowering(portResult, loweredResult);
3655 portType,
"." + port.getName().str() +
".wire");
3659 (void)setLowering(portResult, wire);
3661 operands.push_back(wire);
3668 auto innerSym = oldInstance.getInnerSymAttr();
3669 if (oldInstance.getLowerToBind()) {
3672 oldInstance.getContext(), oldInstance.getInnerSymAttr(), 0,
3675 auto bindOp = builder.create<sv::BindOp>(theModule.getNameAttr(),
3676 innerSym.getSymName());
3679 if (
auto outputFile = oldInstance->getAttr(
"output_file"))
3680 bindOp->setAttr(
"output_file", outputFile);
3683 circuitState.addBind(bindOp);
3687 auto newInstance = builder.create<hw::InstanceOp>(
3688 newModule, oldInstance.getNameAttr(), operands, parameters, innerSym);
3690 if (oldInstance.getLowerToBind() || oldInstance.getDoNotPrint())
3691 newInstance.setDoNotPrintAttr(builder.getUnitAttr());
3693 if (newInstance.getInnerSymAttr())
3694 if (
auto forceName = circuitState.instanceForceNames.lookup(
3695 {cast<hw::HWModuleOp>(newInstance->getParentOp()).getNameAttr(),
3696 newInstance.getInnerNameAttr()}))
3697 newInstance->setAttr(
"hw.verilogName", forceName);
3701 unsigned resultNo = 0;
3702 for (
size_t portIndex = 0, e = portInfo.size(); portIndex != e; ++portIndex) {
3703 auto &port = portInfo[portIndex];
3707 Value resultVal = newInstance.getResult(resultNo);
3709 auto oldPortResult = oldInstance.getResult(portIndex);
3710 (void)setLowering(oldPortResult, resultVal);
3716LogicalResult FIRRTLLowering::visitDecl(ContractOp oldOp) {
3717 SmallVector<Value> inputs;
3718 SmallVector<Type> types;
3719 for (
auto input : oldOp.getInputs()) {
3720 auto lowered = getLoweredValue(input);
3723 inputs.push_back(lowered);
3724 types.push_back(lowered.getType());
3727 auto newOp = builder.create<verif::ContractOp>(types, inputs);
3728 newOp->setDiscardableAttrs(oldOp->getDiscardableAttrDictionary());
3729 auto &body = newOp.getBody().emplaceBlock();
3731 for (
auto [newResult, oldResult, oldArg] :
3732 llvm::zip(newOp.getResults(), oldOp.getResults(),
3733 oldOp.getBody().getArguments())) {
3734 if (failed(setLowering(oldResult, newResult)))
3736 if (failed(setLowering(oldArg, newResult)))
3740 body.getOperations().splice(body.end(),
3741 oldOp.getBody().front().getOperations());
3742 addToWorklist(body);
3752LogicalResult FIRRTLLowering::lowerNoopCast(Operation *op) {
3753 auto operand = getPossiblyInoutLoweredValue(op->getOperand(0));
3758 return setLowering(op->getResult(0), operand);
3761LogicalResult FIRRTLLowering::visitExpr(AsSIntPrimOp op) {
3762 if (isa<ClockType>(op.getInput().getType()))
3763 return setLowering(op->getResult(0),
3764 getLoweredNonClockValue(op.getInput()));
3765 return lowerNoopCast(op);
3768LogicalResult FIRRTLLowering::visitExpr(AsUIntPrimOp op) {
3769 if (isa<ClockType>(op.getInput().getType()))
3770 return setLowering(op->getResult(0),
3771 getLoweredNonClockValue(op.getInput()));
3772 return lowerNoopCast(op);
3775LogicalResult FIRRTLLowering::visitExpr(AsClockPrimOp op) {
3776 return setLoweringTo<seq::ToClockOp>(op, getLoweredValue(op.getInput()));
3779LogicalResult FIRRTLLowering::visitUnrealizedConversionCast(
3780 mlir::UnrealizedConversionCastOp op) {
3782 if (op.getNumOperands() != 1 || op.getNumResults() != 1)
3785 auto operand = op.getOperand(0);
3786 auto result = op.getResult(0);
3789 if (type_isa<FIRRTLType>(operand.getType()) &&
3790 type_isa<FIRRTLType>(result.getType()))
3791 return lowerNoopCast(op);
3795 if (!type_isa<FIRRTLType>(operand.getType())) {
3796 if (type_isa<FIRRTLType>(result.getType()))
3797 return setLowering(result, getPossiblyInoutLoweredValue(operand));
3803 auto loweredResult = getLoweredValue(operand);
3804 if (!loweredResult) {
3807 if (operand.getType().isSignlessInteger(0)) {
3808 return setLowering(result, Value());
3815 result.replaceAllUsesWith(loweredResult);
3819LogicalResult FIRRTLLowering::visitExpr(HWStructCastOp op) {
3822 if (
auto opStructType = dyn_cast<hw::StructType>(op.getOperand().getType()))
3823 return setLowering(op, op.getOperand());
3827 auto result = getLoweredValue(op.getOperand());
3833 op.replaceAllUsesWith(result);
3837LogicalResult FIRRTLLowering::visitExpr(BitCastOp op) {
3838 auto operand = getLoweredValue(op.getOperand());
3841 auto resultType =
lowerType(op.getType());
3845 return setLoweringTo<hw::BitcastOp>(op, resultType, operand);
3848LogicalResult FIRRTLLowering::visitExpr(CvtPrimOp op) {
3849 auto operand = getLoweredValue(op.getOperand());
3853 if (type_cast<IntType>(op.getOperand().getType()).isUnsigned())
3854 return setLowering(op, getOrCreateIntConstant(1, 0));
3856 return setLowering(op, Value());
3861 if (type_cast<IntType>(op.getOperand().getType()).isSigned())
3862 return setLowering(op, operand);
3865 auto zero = getOrCreateIntConstant(1, 0);
3866 return setLoweringTo<comb::ConcatOp>(op, zero, operand);
3869LogicalResult FIRRTLLowering::visitExpr(NotPrimOp op) {
3870 auto operand = getLoweredValue(op.getInput());
3874 auto allOnes = getOrCreateIntConstant(
3875 APInt::getAllOnes(operand.getType().getIntOrFloatBitWidth()));
3876 return setLoweringTo<comb::XorOp>(op, operand, allOnes,
true);
3879LogicalResult FIRRTLLowering::visitExpr(NegPrimOp op) {
3882 auto operand = getLoweredAndExtendedValue(op.getInput(), op.getType());
3886 auto resultType =
lowerType(op.getType());
3888 auto zero = getOrCreateIntConstant(resultType.getIntOrFloatBitWidth(), 0);
3889 return setLoweringTo<comb::SubOp>(op, zero, operand,
true);
3893LogicalResult FIRRTLLowering::visitExpr(PadPrimOp op) {
3894 auto operand = getLoweredAndExtendedValue(op.getInput(), op.getType());
3897 return setLowering(op, operand);
3900LogicalResult FIRRTLLowering::visitExpr(XorRPrimOp op) {
3901 auto operand = getLoweredValue(op.getInput());
3904 return setLowering(op, getOrCreateIntConstant(1, 0));
3909 return setLoweringTo<comb::ParityOp>(op, builder.getIntegerType(1), operand,
3913LogicalResult FIRRTLLowering::visitExpr(AndRPrimOp op) {
3914 auto operand = getLoweredValue(op.getInput());
3917 return setLowering(op, getOrCreateIntConstant(1, 1));
3922 return setLoweringTo<comb::ICmpOp>(
3923 op, ICmpPredicate::eq, operand,
3924 getOrCreateIntConstant(
3925 APInt::getAllOnes(operand.getType().getIntOrFloatBitWidth())),
3929LogicalResult FIRRTLLowering::visitExpr(OrRPrimOp op) {
3930 auto operand = getLoweredValue(op.getInput());
3933 return setLowering(op, getOrCreateIntConstant(1, 0));
3939 return setLoweringTo<comb::ICmpOp>(
3940 op, ICmpPredicate::ne, operand,
3941 getOrCreateIntConstant(operand.getType().getIntOrFloatBitWidth(), 0),
3949template <
typename ResultOpType>
3950LogicalResult FIRRTLLowering::lowerBinOpToVariadic(Operation *op) {
3951 auto resultType = op->getResult(0).getType();
3952 auto lhs = getLoweredAndExtendedValue(op->getOperand(0), resultType);
3953 auto rhs = getLoweredAndExtendedValue(op->getOperand(1), resultType);
3957 return setLoweringTo<ResultOpType>(op, lhs, rhs,
true);
3963template <
typename ResultOpType>
3964LogicalResult FIRRTLLowering::lowerElementwiseLogicalOp(Operation *op) {
3965 auto resultType = op->getResult(0).getType();
3966 auto lhs = getLoweredAndExtendedValue(op->getOperand(0), resultType);
3967 auto rhs = getLoweredAndExtendedValue(op->getOperand(1), resultType);
3978 auto intType = builder.getIntegerType(*bitwidth);
3979 auto retType = lhs.getType();
3982 auto result = builder.createOrFold<ResultOpType>(lhs, rhs,
true);
3983 return setLoweringTo<hw::BitcastOp>(op, retType, result);
3988template <
typename ResultUn
signedOpType,
typename ResultSignedOpType>
3989LogicalResult FIRRTLLowering::lowerBinOp(Operation *op) {
3991 auto resultType = op->getResult(0).getType();
3992 auto lhs = getLoweredAndExtendedValue(op->getOperand(0), resultType);
3993 auto rhs = getLoweredAndExtendedValue(op->getOperand(1), resultType);
3998 if (type_cast<IntType>(resultType).isSigned())
3999 return setLoweringTo<ResultSignedOpType>(op, lhs, rhs,
true);
4000 return setLoweringTo<ResultUnsignedOpType>(op, lhs, rhs,
true);
4005LogicalResult FIRRTLLowering::lowerCmpOp(Operation *op, ICmpPredicate signedOp,
4006 ICmpPredicate unsignedOp) {
4008 auto lhsIntType = type_cast<IntType>(op->getOperand(0).getType());
4009 auto rhsIntType = type_cast<IntType>(op->getOperand(1).getType());
4010 if (!lhsIntType.hasWidth() || !rhsIntType.hasWidth())
4014 if (cmpType.getWidth() == 0)
4015 cmpType = UIntType::get(builder.getContext(), 1);
4016 auto lhs = getLoweredAndExtendedValue(op->getOperand(0), cmpType);
4017 auto rhs = getLoweredAndExtendedValue(op->getOperand(1), cmpType);
4022 Type resultType = builder.getIntegerType(1);
4023 return setLoweringTo<comb::ICmpOp>(
4024 op, resultType, lhsIntType.isSigned() ? signedOp : unsignedOp, lhs, rhs,
4030template <
typename SignedOp,
typename Un
signedOp>
4031LogicalResult FIRRTLLowering::lowerDivLikeOp(Operation *op) {
4035 auto opType = type_cast<IntType>(op->getResult(0).getType());
4036 if (opType.getWidth() == 0)
4037 return setLowering(op->getResult(0), Value());
4041 auto lhs = getLoweredAndExtendedValue(op->getOperand(0), resultType);
4042 auto rhs = getLoweredAndExtendedValue(op->getOperand(1), resultType);
4047 if (opType.isSigned())
4048 result = builder.createOrFold<SignedOp>(lhs, rhs,
true);
4050 result = builder.createOrFold<UnsignedOp>(lhs, rhs,
true);
4052 if (
auto *definingOp = result.getDefiningOp())
4055 if (resultType == opType)
4056 return setLowering(op->getResult(0), result);
4057 return setLoweringTo<comb::ExtractOp>(op,
lowerType(opType), result, 0);
4060LogicalResult FIRRTLLowering::visitExpr(CatPrimOp op) {
4061 auto lhs = getLoweredValue(op.getLhs());
4062 auto rhs = getLoweredValue(op.getRhs());
4066 return setLowering(op, rhs);
4068 return handleZeroBit(op.getRhs(),
4069 [&]() { return setLowering(op, Value()); });
4074 return handleZeroBit(op.getRhs(), [&]() { return setLowering(op, lhs); });
4076 return setLoweringTo<comb::ConcatOp>(op, lhs, rhs);
4083LogicalResult FIRRTLLowering::visitExpr(IsXIntrinsicOp op) {
4084 auto input = getLoweredNonClockValue(op.getArg());
4088 if (!isa<IntType>(input.getType())) {
4089 auto srcType = op.getArg().getType();
4091 assert(bitwidth &&
"Unknown width");
4092 auto intType = builder.getIntegerType(*bitwidth);
4093 input = builder.createOrFold<
hw::BitcastOp>(intType, input);
4096 return setLoweringTo<comb::ICmpOp>(
4097 op, ICmpPredicate::ceq, input,
4098 getOrCreateXConstant(input.getType().getIntOrFloatBitWidth()),
true);
4101LogicalResult FIRRTLLowering::visitStmt(FPGAProbeIntrinsicOp op) {
4102 auto operand = getLoweredValue(op.getInput());
4103 builder.create<hw::WireOp>(operand);
4107LogicalResult FIRRTLLowering::visitExpr(PlusArgsTestIntrinsicOp op) {
4108 return setLoweringTo<sim::PlusArgsTestOp>(op, builder.getIntegerType(1),
4109 op.getFormatStringAttr());
4112LogicalResult FIRRTLLowering::visitExpr(PlusArgsValueIntrinsicOp op) {
4113 auto type =
lowerType(op.getResult().getType());
4117 auto valueOp = builder.create<sim::PlusArgsValueOp>(
4118 builder.getIntegerType(1), type, op.getFormatStringAttr());
4119 if (failed(setLowering(op.getResult(), valueOp.getResult())))
4121 if (failed(setLowering(op.getFound(), valueOp.getFound())))
4126LogicalResult FIRRTLLowering::visitExpr(SizeOfIntrinsicOp op) {
4127 op.emitError(
"SizeOf should have been resolved.");
4131LogicalResult FIRRTLLowering::visitExpr(ClockGateIntrinsicOp op) {
4133 if (op.getTestEnable())
4134 testEnable = getLoweredValue(op.getTestEnable());
4135 return setLoweringTo<seq::ClockGateOp>(
4136 op, getLoweredValue(op.getInput()), getLoweredValue(op.getEnable()),
4137 testEnable, hw::InnerSymAttr{});
4140LogicalResult FIRRTLLowering::visitExpr(ClockInverterIntrinsicOp op) {
4141 auto operand = getLoweredValue(op.getInput());
4142 return setLoweringTo<seq::ClockInverterOp>(op, operand);
4145LogicalResult FIRRTLLowering::visitExpr(ClockDividerIntrinsicOp op) {
4146 auto operand = getLoweredValue(op.getInput());
4147 return setLoweringTo<seq::ClockDividerOp>(op, operand, op.getPow2());
4150LogicalResult FIRRTLLowering::visitExpr(LTLAndIntrinsicOp op) {
4151 return setLoweringToLTL<ltl::AndOp>(
4153 ValueRange{getLoweredValue(op.getLhs()), getLoweredValue(op.getRhs())});
4156LogicalResult FIRRTLLowering::visitExpr(LTLOrIntrinsicOp op) {
4157 return setLoweringToLTL<ltl::OrOp>(
4159 ValueRange{getLoweredValue(op.getLhs()), getLoweredValue(op.getRhs())});
4162LogicalResult FIRRTLLowering::visitExpr(LTLIntersectIntrinsicOp op) {
4163 return setLoweringToLTL<ltl::IntersectOp>(
4165 ValueRange{getLoweredValue(op.getLhs()), getLoweredValue(op.getRhs())});
4168LogicalResult FIRRTLLowering::visitExpr(LTLDelayIntrinsicOp op) {
4169 return setLoweringToLTL<ltl::DelayOp>(op, getLoweredValue(op.getInput()),
4170 op.getDelayAttr(), op.getLengthAttr());
4173LogicalResult FIRRTLLowering::visitExpr(LTLConcatIntrinsicOp op) {
4174 return setLoweringToLTL<ltl::ConcatOp>(
4176 ValueRange{getLoweredValue(op.getLhs()), getLoweredValue(op.getRhs())});
4179LogicalResult FIRRTLLowering::visitExpr(LTLRepeatIntrinsicOp op) {
4180 return setLoweringToLTL<ltl::RepeatOp>(op, getLoweredValue(op.getInput()),
4181 op.getBaseAttr(), op.getMoreAttr());
4184LogicalResult FIRRTLLowering::visitExpr(LTLGoToRepeatIntrinsicOp op) {
4185 return setLoweringToLTL<ltl::GoToRepeatOp>(
4186 op, getLoweredValue(op.getInput()), op.getBaseAttr(), op.getMoreAttr());
4189LogicalResult FIRRTLLowering::visitExpr(LTLNonConsecutiveRepeatIntrinsicOp op) {
4190 return setLoweringToLTL<ltl::NonConsecutiveRepeatOp>(
4191 op, getLoweredValue(op.getInput()), op.getBaseAttr(), op.getMoreAttr());
4194LogicalResult FIRRTLLowering::visitExpr(LTLNotIntrinsicOp op) {
4195 return setLoweringToLTL<ltl::NotOp>(op, getLoweredValue(op.getInput()));
4198LogicalResult FIRRTLLowering::visitExpr(LTLImplicationIntrinsicOp op) {
4199 return setLoweringToLTL<ltl::ImplicationOp>(
4201 ValueRange{getLoweredValue(op.getLhs()), getLoweredValue(op.getRhs())});
4204LogicalResult FIRRTLLowering::visitExpr(LTLUntilIntrinsicOp op) {
4205 return setLoweringToLTL<ltl::UntilOp>(
4207 ValueRange{getLoweredValue(op.getLhs()), getLoweredValue(op.getRhs())});
4210LogicalResult FIRRTLLowering::visitExpr(LTLEventuallyIntrinsicOp op) {
4211 return setLoweringToLTL<ltl::EventuallyOp>(op,
4212 getLoweredValue(op.getInput()));
4215LogicalResult FIRRTLLowering::visitExpr(LTLClockIntrinsicOp op) {
4216 return setLoweringToLTL<ltl::ClockOp>(op, getLoweredValue(op.getInput()),
4217 ltl::ClockEdge::Pos,
4218 getLoweredNonClockValue(op.getClock()));
4221template <
typename TargetOp,
typename IntrinsicOp>
4222LogicalResult FIRRTLLowering::lowerVerifIntrinsicOp(IntrinsicOp op) {
4223 auto property = getLoweredValue(op.getProperty());
4224 auto enable = op.getEnable() ? getLoweredValue(op.getEnable()) : Value();
4225 builder.create<TargetOp>(property, enable, op.getLabelAttr());
4229LogicalResult FIRRTLLowering::visitStmt(VerifAssertIntrinsicOp op) {
4230 return lowerVerifIntrinsicOp<verif::AssertOp>(op);
4233LogicalResult FIRRTLLowering::visitStmt(VerifAssumeIntrinsicOp op) {
4234 return lowerVerifIntrinsicOp<verif::AssumeOp>(op);
4237LogicalResult FIRRTLLowering::visitStmt(VerifCoverIntrinsicOp op) {
4238 return lowerVerifIntrinsicOp<verif::CoverOp>(op);
4241LogicalResult FIRRTLLowering::visitStmt(VerifRequireIntrinsicOp op) {
4242 if (!isa<verif::ContractOp>(op->getParentOp()))
4243 return lowerVerifIntrinsicOp<verif::AssertOp>(op);
4244 return lowerVerifIntrinsicOp<verif::RequireOp>(op);
4247LogicalResult FIRRTLLowering::visitStmt(VerifEnsureIntrinsicOp op) {
4248 if (!isa<verif::ContractOp>(op->getParentOp()))
4249 return lowerVerifIntrinsicOp<verif::AssertOp>(op);
4250 return lowerVerifIntrinsicOp<verif::EnsureOp>(op);
4253LogicalResult FIRRTLLowering::visitExpr(HasBeenResetIntrinsicOp op) {
4254 auto clock = getLoweredNonClockValue(op.getClock());
4255 auto reset = getLoweredValue(op.getReset());
4256 if (!clock || !reset)
4258 auto resetType = op.getReset().getType();
4259 auto uintResetType = dyn_cast<UIntType>(resetType);
4260 auto isSync = uintResetType && uintResetType.getWidth() == 1;
4261 auto isAsync = isa<AsyncResetType>(resetType);
4262 if (!isAsync && !isSync) {
4263 auto d = op.emitError(
"uninferred reset passed to 'has_been_reset'; "
4264 "requires sync or async reset");
4265 d.attachNote() <<
"reset is of type " << resetType
4266 <<
", should be '!firrtl.uint<1>' or '!firrtl.asyncreset'";
4269 return setLoweringTo<verif::HasBeenResetOp>(op, clock, reset, isAsync);
4276LogicalResult FIRRTLLowering::visitExpr(BitsPrimOp op) {
4277 auto input = getLoweredValue(op.getInput());
4281 Type resultType = builder.getIntegerType(op.getHi() - op.getLo() + 1);
4282 return setLoweringTo<comb::ExtractOp>(op, resultType, input, op.getLo());
4285LogicalResult FIRRTLLowering::visitExpr(InvalidValueOp op) {
4286 auto resultTy =
lowerType(op.getType());
4293 if (type_isa<AnalogType>(op.getType()))
4296 return setLoweringTo<sv::WireOp>(op, resultTy,
".invalid_analog");
4299 if (type_cast<FIRRTLBaseType>(op.getType()).containsAnalog())
4310 auto constant = getOrCreateIntConstant(*bitwidth, 0);
4312 if (!type_isa<IntegerType>(resultTy))
4313 constant = builder.create<
hw::BitcastOp>(resultTy, constant);
4314 return setLowering(op, constant);
4318 op.emitOpError(
"unsupported type");
4322LogicalResult FIRRTLLowering::visitExpr(HeadPrimOp op) {
4323 auto input = getLoweredValue(op.getInput());
4326 auto inWidth = type_cast<IntegerType>(input.getType()).getWidth();
4327 if (op.getAmount() == 0)
4328 return setLowering(op, Value());
4329 Type resultType = builder.getIntegerType(op.getAmount());
4330 return setLoweringTo<comb::ExtractOp>(op, resultType, input,
4331 inWidth - op.getAmount());
4334LogicalResult FIRRTLLowering::visitExpr(ShlPrimOp op) {
4335 auto input = getLoweredValue(op.getInput());
4338 if (op.getAmount() == 0)
4340 return setLowering(op, getOrCreateIntConstant(op.getAmount(), 0));
4345 if (op.getAmount() == 0)
4346 return setLowering(op, input);
4348 auto zero = getOrCreateIntConstant(op.getAmount(), 0);
4349 return setLoweringTo<comb::ConcatOp>(op, input, zero);
4352LogicalResult FIRRTLLowering::visitExpr(ShrPrimOp op) {
4353 auto input = getLoweredValue(op.getInput());
4358 auto inWidth = type_cast<IntegerType>(input.getType()).getWidth();
4359 auto shiftAmount = op.getAmount();
4360 if (shiftAmount >= inWidth) {
4362 if (type_cast<IntType>(op.getInput().getType()).isUnsigned())
4363 return setLowering(op, {});
4366 shiftAmount = inWidth - 1;
4369 Type resultType = builder.getIntegerType(inWidth - shiftAmount);
4370 return setLoweringTo<comb::ExtractOp>(op, resultType, input, shiftAmount);
4373LogicalResult FIRRTLLowering::visitExpr(TailPrimOp op) {
4374 auto input = getLoweredValue(op.getInput());
4378 auto inWidth = type_cast<IntegerType>(input.getType()).getWidth();
4379 if (inWidth == op.getAmount())
4380 return setLowering(op, Value());
4381 Type resultType = builder.getIntegerType(inWidth - op.getAmount());
4382 return setLoweringTo<comb::ExtractOp>(op, resultType, input, 0);
4385LogicalResult FIRRTLLowering::visitExpr(MuxPrimOp op) {
4386 auto cond = getLoweredValue(op.getSel());
4387 auto ifTrue = getLoweredAndExtendedValue(op.getHigh(), op.getType());
4388 auto ifFalse = getLoweredAndExtendedValue(op.getLow(), op.getType());
4389 if (!cond || !ifTrue || !ifFalse)
4392 if (isa<ClockType>(op.getType()))
4393 return setLoweringTo<seq::ClockMuxOp>(op, cond, ifTrue, ifFalse);
4394 return setLoweringTo<comb::MuxOp>(op, ifTrue.getType(), cond, ifTrue, ifFalse,
4398LogicalResult FIRRTLLowering::visitExpr(Mux2CellIntrinsicOp op) {
4399 auto cond = getLoweredValue(op.getSel());
4400 auto ifTrue = getLoweredAndExtendedValue(op.getHigh(), op.getType());
4401 auto ifFalse = getLoweredAndExtendedValue(op.getLow(), op.getType());
4402 if (!cond || !ifTrue || !ifFalse)
4405 auto val = builder.create<
comb::MuxOp>(ifTrue.getType(), cond, ifTrue,
4407 return setLowering(op, createValueWithMuxAnnotation(val,
true));
4410LogicalResult FIRRTLLowering::visitExpr(Mux4CellIntrinsicOp op) {
4411 auto sel = getLoweredValue(op.getSel());
4412 auto v3 = getLoweredAndExtendedValue(op.getV3(), op.getType());
4413 auto v2 = getLoweredAndExtendedValue(op.getV2(), op.getType());
4414 auto v1 = getLoweredAndExtendedValue(op.getV1(), op.getType());
4415 auto v0 = getLoweredAndExtendedValue(op.getV0(), op.getType());
4416 if (!sel || !v3 || !v2 || !v1 || !v0)
4418 Value array[] = {v3, v2, v1, v0};
4421 return setLowering(op, createValueWithMuxAnnotation(val,
false));
4440Value FIRRTLLowering::createValueWithMuxAnnotation(Operation *op,
bool isMux2) {
4441 assert(op->getNumResults() == 1 &&
"only expect a single result");
4442 auto val = op->getResult(0);
4446 op, sv::SVAttributeAttr::get(builder.getContext(),
"cadence map_to_mux",
4453 OpBuilder::InsertionGuard guard(builder);
4454 builder.setInsertionPoint(op);
4455 StringRef namehint = isMux2 ?
"mux2cell_in" :
"mux4cell_in";
4456 for (
auto [idx, operand] :
llvm::enumerate(op->getOperands())) {
4458 op->getContext(),
nullptr, 0,
4461 builder.create<hw::WireOp>(operand, namehint + Twine(idx), innerSym);
4462 op->setOperand(idx, wire);
4466 auto assignOp = builder.create<
sv::AssignOp>(valWire, val);
4467 sv::setSVAttributes(assignOp,
4468 sv::SVAttributeAttr::get(builder.getContext(),
4469 "synopsys infer_mux_override",
4474Value FIRRTLLowering::createArrayIndexing(Value array, Value index) {
4476 auto size = hw::type_cast<hw::ArrayType>(array.getType()).getNumElements();
4481 if (!llvm::isPowerOf2_64(size)) {
4482 auto extElem = getOrCreateIntConstant(APInt(llvm::Log2_64_Ceil(size), 0));
4484 SmallVector<Value> temp(llvm::NextPowerOf2(size) - size, extValue);
4486 Value temp2[] = {ext.getResult(), array};
4492 return inBoundsRead;
4495LogicalResult FIRRTLLowering::visitExpr(MultibitMuxOp op) {
4497 auto index = getLoweredAndExtOrTruncValue(
4499 UIntType::get(op.getContext(),
4504 SmallVector<Value> loweredInputs;
4505 loweredInputs.reserve(op.getInputs().size());
4506 for (
auto input : op.getInputs()) {
4507 auto lowered = getLoweredAndExtendedValue(input, op.getType());
4510 loweredInputs.push_back(lowered);
4514 return setLowering(op, createArrayIndexing(array, index));
4517LogicalResult FIRRTLLowering::visitExpr(VerbatimExprOp op) {
4518 auto resultTy =
lowerType(op.getType());
4522 SmallVector<Value, 4> operands;
4523 operands.reserve(op.getSubstitutions().size());
4524 for (
auto operand : op.getSubstitutions()) {
4525 auto lowered = getLoweredValue(operand);
4528 operands.push_back(lowered);
4531 ArrayAttr symbols = op.getSymbolsAttr();
4533 symbols = ArrayAttr::get(op.getContext(), {});
4535 return setLoweringTo<sv::VerbatimExprOp>(op, resultTy, op.getTextAttr(),
4539LogicalResult FIRRTLLowering::visitExpr(XMRRefOp op) {
4543 Type baseType = op.getType().getType();
4546 if (isa<ClockType>(baseType))
4547 xmrType = builder.getIntegerType(1);
4551 return setLoweringTo<sv::XMRRefOp>(op, sv::InOutType::get(xmrType),
4552 op.getRef(), op.getVerbatimSuffixAttr());
4555LogicalResult FIRRTLLowering::visitExpr(XMRDerefOp op) {
4559 if (isa<ClockType>(op.getType()))
4560 xmrType = builder.getIntegerType(1);
4564 auto xmr = builder.create<sv::XMRRefOp>(
4565 sv::InOutType::get(xmrType), op.getRef(), op.getVerbatimSuffixAttr());
4566 auto readXmr = getReadValue(xmr);
4567 if (!isa<ClockType>(op.getType()))
4568 return setLowering(op, readXmr);
4569 return setLoweringTo<seq::ToClockOp>(op, readXmr);
4574LogicalResult FIRRTLLowering::visitExpr(TimeOp op) {
return success(); }
4575LogicalResult FIRRTLLowering::visitExpr(HierarchicalModuleNameOp op) {
4583LogicalResult FIRRTLLowering::visitStmt(SkipOp op) {
4595FailureOr<bool> FIRRTLLowering::lowerConnect(Value destVal, Value srcVal) {
4596 auto srcType = srcVal.getType();
4597 auto dstType = destVal.getType();
4598 if (srcType != dstType &&
4599 (isa<hw::TypeAliasType>(srcType) || isa<hw::TypeAliasType>(dstType))) {
4600 srcVal = builder.create<
hw::BitcastOp>(destVal.getType(), srcVal);
4602 return TypeSwitch<Operation *, FailureOr<bool>>(destVal.getDefiningOp())
4603 .Case<hw::WireOp>([&](
auto op) {
4604 maybeUnused(op.getInput());
4605 op.getInputMutable().assign(srcVal);
4608 .Case<seq::FirRegOp>([&](
auto op) {
4609 maybeUnused(op.getNext());
4610 op.getNextMutable().assign(srcVal);
4613 .Case<hw::StructExtractOp, hw::ArrayGetOp>([](
auto op) {
4616 op.emitOpError(
"used as connect destination");
4619 .Default([](
auto) {
return false; });
4622LogicalResult FIRRTLLowering::visitStmt(ConnectOp op) {
4623 auto dest = op.getDest();
4625 auto destType = type_cast<FIRRTLBaseType>(dest.getType()).getPassiveType();
4626 auto srcVal = getLoweredAndExtendedValue(op.getSrc(), destType);
4628 return handleZeroBit(op.getSrc(), []() { return success(); });
4630 auto destVal = getPossiblyInoutLoweredValue(dest);
4634 auto result = lowerConnect(destVal, srcVal);
4642 if (updateIfBackedge(destVal, srcVal))
4645 if (!isa<hw::InOutType>(destVal.getType()))
4646 return op.emitError(
"destination isn't an inout type");
4652LogicalResult FIRRTLLowering::visitStmt(MatchingConnectOp op) {
4653 auto dest = op.getDest();
4654 auto srcVal = getLoweredValue(op.getSrc());
4656 return handleZeroBit(op.getSrc(), []() { return success(); });
4658 auto destVal = getPossiblyInoutLoweredValue(dest);
4662 auto result = lowerConnect(destVal, srcVal);
4670 if (updateIfBackedge(destVal, srcVal))
4673 if (!isa<hw::InOutType>(destVal.getType()))
4674 return op.emitError(
"destination isn't an inout type");
4680LogicalResult FIRRTLLowering::visitStmt(ForceOp op) {
4681 auto srcVal = getLoweredValue(op.getSrc());
4685 auto destVal = getPossiblyInoutLoweredValue(op.getDest());
4689 if (!isa<hw::InOutType>(destVal.getType()))
4690 return op.emitError(
"destination isn't an inout type");
4693 circuitState.addMacroDecl(builder.getStringAttr(
"SYNTHESIS"));
4694 addToIfDefBlock(
"SYNTHESIS", std::function<
void()>(), [&]() {
4695 addToInitialBlock([&]() { builder.create<sv::ForceOp>(destVal, srcVal); });
4700LogicalResult FIRRTLLowering::visitStmt(RefForceOp op) {
4701 auto src = getLoweredNonClockValue(op.getSrc());
4702 auto clock = getLoweredNonClockValue(op.getClock());
4703 auto pred = getLoweredValue(op.getPredicate());
4704 if (!src || !clock || !pred)
4707 auto destVal = getPossiblyInoutLoweredValue(op.getDest());
4712 circuitState.addMacroDecl(builder.getStringAttr(
"SYNTHESIS"));
4713 addToIfDefBlock(
"SYNTHESIS", std::function<
void()>(), [&]() {
4714 addToAlwaysBlock(clock, [&]() {
4715 addIfProceduralBlock(
4716 pred, [&]() { builder.create<sv::ForceOp>(destVal, src); });
4721LogicalResult FIRRTLLowering::visitStmt(RefForceInitialOp op) {
4722 auto src = getLoweredNonClockValue(op.getSrc());
4723 auto pred = getLoweredValue(op.getPredicate());
4727 auto destVal = getPossiblyInoutLoweredValue(op.getDest());
4732 circuitState.addMacroDecl(builder.getStringAttr(
"SYNTHESIS"));
4733 addToIfDefBlock(
"SYNTHESIS", std::function<
void()>(), [&]() {
4734 addToInitialBlock([&]() {
4735 addIfProceduralBlock(
4736 pred, [&]() { builder.create<sv::ForceOp>(destVal, src); });
4741LogicalResult FIRRTLLowering::visitStmt(RefReleaseOp op) {
4742 auto clock = getLoweredNonClockValue(op.getClock());
4743 auto pred = getLoweredValue(op.getPredicate());
4744 if (!clock || !pred)
4747 auto destVal = getPossiblyInoutLoweredValue(op.getDest());
4752 circuitState.addMacroDecl(builder.getStringAttr(
"SYNTHESIS"));
4753 addToIfDefBlock(
"SYNTHESIS", std::function<
void()>(), [&]() {
4754 addToAlwaysBlock(clock, [&]() {
4755 addIfProceduralBlock(pred,
4756 [&]() { builder.create<sv::ReleaseOp>(destVal); });
4761LogicalResult FIRRTLLowering::visitStmt(RefReleaseInitialOp op) {
4762 auto destVal = getPossiblyInoutLoweredValue(op.getDest());
4763 auto pred = getLoweredValue(op.getPredicate());
4764 if (!destVal || !pred)
4768 circuitState.addMacroDecl(builder.getStringAttr(
"SYNTHESIS"));
4769 addToIfDefBlock(
"SYNTHESIS", std::function<
void()>(), [&]() {
4770 addToInitialBlock([&]() {
4771 addIfProceduralBlock(pred,
4772 [&]() { builder.create<sv::ReleaseOp>(destVal); });
4780 StringRef originalFormatString,
4781 mlir::OperandRange operands,
4782 StringAttr &result) {
4785 SmallString<32> formatString;
4786 for (
size_t i = 0, e = originalFormatString.size(), subIdx = 0; i != e; ++i) {
4787 char c = originalFormatString[i];
4791 formatString.push_back(c);
4794 SmallString<6> width;
4795 c = originalFormatString[++i];
4798 c = originalFormatString[++i];
4809 formatString.append(width);
4815 formatString.push_back(c);
4822 if (originalFormatString.slice(i, i + 4) !=
"{{}}") {
4823 formatString.push_back(c);
4827 auto substitution = operands[subIdx++];
4828 assert(type_isa<FStringType>(substitution.getType()) &&
4829 "the operand for a '{{}}' substitution must be an 'fstring' type");
4831 TypeSwitch<Operation *, LogicalResult>(substitution.getDefiningOp())
4832 .template Case<TimeOp>([&](
auto) {
4833 formatString.append(
"%0t");
4836 .
template Case<HierarchicalModuleNameOp>([&](
auto) {
4837 formatString.append(
"%m");
4840 .Default([&](
auto) {
4841 emitError(loc,
"has a substitution with an unimplemented "
4843 .attachNote(substitution.getLoc())
4844 <<
"op with an unimplemented lowering is here";
4854 formatString.push_back(c);
4858 result = StringAttr::get(loc->getContext(), formatString);
4865LogicalResult FIRRTLLowering::visitPrintfLike(
4866 T op,
const FileDescriptorInfo &fileDescriptorInfo,
bool usePrintfCond) {
4867 auto clock = getLoweredNonClockValue(op.getClock());
4868 auto cond = getLoweredValue(op.getCond());
4869 if (!clock || !cond)
4872 StringAttr formatString;
4874 op.getSubstitutions(), formatString)))
4877 auto fn = [&](Value fd) {
4878 SmallVector<Value> operands;
4879 if (failed(loweredFmtOperands(op.getSubstitutions(), operands)))
4881 builder.create<sv::FWriteOp>(op.getLoc(), fd, formatString, operands);
4885 return lowerStatementWithFd(fileDescriptorInfo, clock, cond, fn,
4889LogicalResult FIRRTLLowering::visitStmt(FPrintFOp op) {
4890 StringAttr outputFileAttr;
4892 op.getOutputFileSubstitutions(),
4896 FileDescriptorInfo outputFile(outputFileAttr,
4897 op.getOutputFileSubstitutions());
4898 return visitPrintfLike(op, outputFile,
false);
4902LogicalResult FIRRTLLowering::visitStmt(FFlushOp op) {
4903 auto clock = getLoweredNonClockValue(op.getClock());
4904 auto cond = getLoweredValue(op.getCond());
4905 if (!clock || !cond)
4908 auto fn = [&](Value fd) {
4909 builder.create<sv::FFlushOp>(op.getLoc(), fd);
4913 if (!op.getOutputFileAttr())
4914 return lowerStatementWithFd({}, clock, cond, fn,
false);
4918 StringAttr outputFileAttr;
4920 op.getOutputFileSubstitutions(),
4924 return lowerStatementWithFd(
4925 FileDescriptorInfo(outputFileAttr, op.getOutputFileSubstitutions()),
4926 clock, cond, fn,
false);
4931LogicalResult FIRRTLLowering::visitStmt(StopOp op) {
4932 auto clock = getLoweredValue(op.getClock());
4933 auto cond = getLoweredValue(op.getCond());
4934 if (!clock || !cond)
4937 circuitState.usedStopCond =
true;
4938 circuitState.addFragment(theModule,
"STOP_COND_FRAGMENT");
4941 builder.create<sv::MacroRefExprOp>(cond.getType(),
"STOP_COND_");
4942 Value exitCond = builder.createOrFold<
comb::AndOp>(stopCond, cond,
true);
4944 if (op.getExitCode())
4945 builder.create<sim::FatalOp>(clock, exitCond);
4947 builder.create<sim::FinishOp>(clock, exitCond);
4955template <
typename... Args>
4957 StringRef opName, Args &&...args) {
4958 if (opName ==
"assert")
4959 return builder.create<sv::AssertOp>(std::forward<Args>(args)...);
4960 if (opName ==
"assume")
4961 return builder.create<sv::AssumeOp>(std::forward<Args>(args)...);
4962 if (opName ==
"cover")
4963 return builder.create<sv::CoverOp>(std::forward<Args>(args)...);
4964 llvm_unreachable(
"unknown verification op");
4970template <
typename... Args>
4972 StringRef opName, Args &&...args) {
4973 if (opName ==
"assert")
4974 return builder.create<sv::AssertConcurrentOp>(std::forward<Args>(args)...);
4975 if (opName ==
"assume")
4976 return builder.create<sv::AssumeConcurrentOp>(std::forward<Args>(args)...);
4977 if (opName ==
"cover")
4978 return builder.create<sv::CoverConcurrentOp>(std::forward<Args>(args)...);
4979 llvm_unreachable(
"unknown verification op");
5000LogicalResult FIRRTLLowering::lowerVerificationStatement(
5001 Operation *op, StringRef labelPrefix, Value opClock, Value opPredicate,
5002 Value opEnable, StringAttr opMessageAttr, ValueRange opOperands,
5003 StringAttr opNameAttr,
bool isConcurrent, EventControl opEventControl) {
5004 StringRef opName = op->getName().stripDialect();
5007 ArrayRef<Attribute> guards{};
5008 if (
auto guardsAttr = op->template getAttrOfType<ArrayAttr>(
"guards"))
5009 guards = guardsAttr.getValue();
5011 auto isCover = isa<CoverOp>(op);
5012 auto clock = getLoweredNonClockValue(opClock);
5013 auto enable = getLoweredValue(opEnable);
5014 auto predicate = getLoweredValue(opPredicate);
5015 if (!clock || !enable || !predicate)
5019 if (opNameAttr && !opNameAttr.getValue().empty())
5021 StringAttr prefixedLabel;
5024 StringAttr::get(builder.getContext(), labelPrefix + label.getValue());
5027 SmallVector<Value> messageOps;
5031 if (flavor == VerificationFlavor::IfElseFatal && !isa<AssertOp>(op))
5032 flavor = VerificationFlavor::None;
5034 if (flavor == VerificationFlavor::None) {
5038 auto format = op->getAttrOfType<StringAttr>(
"format");
5040 if (isConcurrent && format && format.getValue() ==
"ifElseFatal") {
5041 if (!isa<AssertOp>(op))
5042 return op->emitError()
5043 <<
"ifElseFatal format cannot be used for non-assertions";
5044 flavor = VerificationFlavor::IfElseFatal;
5045 }
else if (isConcurrent)
5046 flavor = VerificationFlavor::SVA;
5048 flavor = VerificationFlavor::Immediate;
5051 if (!isCover && opMessageAttr && !opMessageAttr.getValue().empty()) {
5052 message = opMessageAttr;
5053 if (failed(loweredFmtOperands(opOperands, messageOps)))
5056 if (flavor == VerificationFlavor::SVA) {
5061 for (
auto &loweredValue : messageOps)
5062 loweredValue = builder.create<
sv::SampledOp>(loweredValue);
5068 case VerificationFlavor::Immediate: {
5070 auto deferImmediate = circt::sv::DeferAssertAttr::get(
5071 builder.getContext(), circt::sv::DeferAssert::Immediate);
5072 addToAlwaysBlock(clock, [&]() {
5073 addIfProceduralBlock(enable, [&]() {
5075 prefixedLabel, message, messageOps);
5080 case VerificationFlavor::IfElseFatal: {
5081 assert(isa<AssertOp>(op) &&
"only assert is expected");
5084 auto boolType = IntegerType::get(builder.getContext(), 1);
5085 predicate = comb::createOrFoldNot(predicate, builder,
true);
5086 predicate = builder.createOrFold<
comb::AndOp>(enable, predicate,
true);
5088 circuitState.addMacroDecl(builder.getStringAttr(
"SYNTHESIS"));
5089 addToIfDefBlock(
"SYNTHESIS", {}, [&]() {
5090 addToAlwaysBlock(clock, [&]() {
5091 addIfProceduralBlock(predicate, [&]() {
5092 circuitState.usedStopCond =
true;
5093 circuitState.addFragment(theModule,
"STOP_COND_FRAGMENT");
5095 circuitState.usedAssertVerboseCond =
true;
5096 circuitState.addFragment(theModule,
"ASSERT_VERBOSE_COND_FRAGMENT");
5098 addIfProceduralBlock(
5099 builder.create<sv::MacroRefExprOp>(boolType,
5100 "ASSERT_VERBOSE_COND_"),
5101 [&]() { builder.create<sv::ErrorOp>(message, messageOps); });
5102 addIfProceduralBlock(
5103 builder.create<sv::MacroRefExprOp>(boolType,
"STOP_COND_"),
5104 [&]() { builder.create<sv::FatalOp>(); });
5110 case VerificationFlavor::SVA: {
5115 comb::createOrFoldNot(enable, builder,
true);
5117 builder.createOrFold<
comb::OrOp>(notEnable, predicate,
true);
5119 predicate = builder.createOrFold<
comb::AndOp>(enable, predicate,
true);
5123 sv::EventControl event;
5124 switch (opEventControl) {
5125 case EventControl::AtPosEdge:
5126 event = circt::sv::EventControl::AtPosEdge;
5128 case EventControl::AtEdge:
5129 event = circt::sv::EventControl::AtEdge;
5131 case EventControl::AtNegEdge:
5132 event = circt::sv::EventControl::AtNegEdge;
5138 circt::sv::EventControlAttr::get(builder.getContext(), event), clock,
5139 predicate, prefixedLabel, message, messageOps);
5142 case VerificationFlavor::None:
5144 "flavor `None` must be converted into one of concreate flavors");
5151 return emitGuards(op->getLoc(), guards,
emit);
5155LogicalResult FIRRTLLowering::visitStmt(AssertOp op) {
5156 return lowerVerificationStatement(
5157 op,
"assert__", op.getClock(), op.getPredicate(), op.getEnable(),
5158 op.getMessageAttr(), op.getSubstitutions(), op.getNameAttr(),
5159 op.getIsConcurrent(), op.getEventControl());
5163LogicalResult FIRRTLLowering::visitStmt(AssumeOp op) {
5164 return lowerVerificationStatement(
5165 op,
"assume__", op.getClock(), op.getPredicate(), op.getEnable(),
5166 op.getMessageAttr(), op.getSubstitutions(), op.getNameAttr(),
5167 op.getIsConcurrent(), op.getEventControl());
5171LogicalResult FIRRTLLowering::visitStmt(CoverOp op) {
5172 return lowerVerificationStatement(
5173 op,
"cover__", op.getClock(), op.getPredicate(), op.getEnable(),
5174 op.getMessageAttr(), op.getSubstitutions(), op.getNameAttr(),
5175 op.getIsConcurrent(), op.getEventControl());
5179LogicalResult FIRRTLLowering::visitStmt(UnclockedAssumeIntrinsicOp op) {
5184 auto guardsAttr = op->getAttrOfType<mlir::ArrayAttr>(
"guards");
5185 ArrayRef<Attribute> guards =
5186 guardsAttr ? guardsAttr.getValue() : ArrayRef<Attribute>();
5188 auto label = op.getNameAttr();
5189 StringAttr assumeLabel;
5190 if (label && !label.empty())
5192 StringAttr::get(builder.getContext(),
"assume__" + label.getValue());
5193 auto predicate = getLoweredValue(op.getPredicate());
5194 auto enable = getLoweredValue(op.getEnable());
5195 auto notEnable = comb::createOrFoldNot(enable, builder,
true);
5196 predicate = builder.createOrFold<
comb::OrOp>(notEnable, predicate,
true);
5198 SmallVector<Value> messageOps;
5199 for (
auto operand : op.getSubstitutions()) {
5200 auto loweredValue = getLoweredValue(operand);
5201 if (!loweredValue) {
5205 loweredValue = getOrCreateIntConstant(1, 0);
5207 messageOps.push_back(loweredValue);
5209 return emitGuards(op.getLoc(), guards, [&]() {
5210 builder.create<sv::AlwaysOp>(
5211 ArrayRef(sv::EventControl::AtEdge), ArrayRef(predicate), [&]() {
5212 if (op.getMessageAttr().getValue().empty())
5213 buildImmediateVerifOp(
5214 builder,
"assume", predicate,
5215 circt::sv::DeferAssertAttr::get(
5216 builder.getContext(), circt::sv::DeferAssert::Immediate),
5219 buildImmediateVerifOp(
5220 builder,
"assume", predicate,
5221 circt::sv::DeferAssertAttr::get(
5222 builder.getContext(), circt::sv::DeferAssert::Immediate),
5223 assumeLabel, op.getMessageAttr(), messageOps);
5228LogicalResult FIRRTLLowering::visitStmt(AttachOp op) {
5230 if (op.getAttached().size() < 2)
5233 SmallVector<Value, 4> inoutValues;
5234 for (
auto v : op.getAttached()) {
5235 inoutValues.push_back(getPossiblyInoutLoweredValue(v));
5236 if (!inoutValues.back()) {
5240 inoutValues.pop_back();
5244 if (!isa<hw::InOutType>(inoutValues.back().getType()))
5245 return op.emitError(
"operand isn't an inout type");
5248 if (inoutValues.size() < 2)
5259 bool isAttachInternalOnly =
5260 llvm::none_of(inoutValues, [](
auto v) {
return isa<BlockArgument>(v); });
5262 if (isAttachInternalOnly) {
5263 auto v0 = inoutValues.front();
5264 for (
auto v : inoutValues) {
5267 v.replaceAllUsesWith(v0);
5274 circuitState.addMacroDecl(builder.getStringAttr(
"SYNTHESIS"));
5275 circuitState.addMacroDecl(builder.getStringAttr(
"VERILATOR"));
5280 SmallVector<Value, 4> values;
5281 for (
auto inoutValue : inoutValues)
5282 values.push_back(getReadValue(inoutValue));
5284 for (
size_t i1 = 0, e = inoutValues.size(); i1 != e; ++i1) {
5285 for (
size_t i2 = 0; i2 != e; ++i2)
5287 builder.create<
sv::AssignOp>(inoutValues[i1], values[i2]);
5296 builder.create<sv::VerbatimOp>(
5297 "`error \"Verilator does not support alias and thus "
5299 "arbitrarily connect bidirectional wires and ports\"");
5301 [&]() { builder.create<sv::AliasOp>(inoutValues); });
5307LogicalResult FIRRTLLowering::visitStmt(BindOp op) {
5308 builder.create<sv::BindOp>(op.getInstanceAttr());
5312LogicalResult FIRRTLLowering::fixupLTLOps() {
5313 if (ltlOpFixupWorklist.empty())
5315 LLVM_DEBUG(llvm::dbgs() <<
"Fixing up " << ltlOpFixupWorklist.size()
5319 for (
unsigned i = 0, e = ltlOpFixupWorklist.size(); i != e; ++i)
5320 for (
auto *user : ltlOpFixupWorklist[i]->getUsers())
5321 if (isa<
hw::WireOp>(user))
5322 ltlOpFixupWorklist.insert(user);
5325 while (!ltlOpFixupWorklist.empty()) {
5326 auto *op = ltlOpFixupWorklist.pop_back_val();
5329 if (
auto opIntf = dyn_cast_or_null<mlir::InferTypeOpInterface>(op)) {
5330 LLVM_DEBUG(llvm::dbgs() <<
"- Update " << *op <<
"\n");
5331 SmallVector<Type, 2> types;
5332 auto result = opIntf.inferReturnTypes(
5333 op->getContext(), op->getLoc(), op->getOperands(),
5334 op->getAttrDictionary(), op->getPropertiesStorage(), op->getRegions(),
5338 assert(types.size() == op->getNumResults());
5342 for (
auto [result, type] :
llvm::zip(op->getResults(), types)) {
5343 if (result.getType() == type)
5345 LLVM_DEBUG(llvm::dbgs()
5346 <<
" - Result #" << result.getResultNumber() <<
" from "
5347 << result.getType() <<
" to " << type <<
"\n");
5348 result.setType(type);
5349 for (
auto *user : result.getUsers())
5351 ltlOpFixupWorklist.insert(user);
5356 if (
auto wireOp = dyn_cast<hw::WireOp>(op)) {
5357 if (isa<ltl::SequenceType, ltl::PropertyType>(wireOp.getType())) {
5358 wireOp.replaceAllUsesWith(wireOp.getInput());
5359 LLVM_DEBUG(llvm::dbgs() <<
"- Remove " << wireOp <<
"\n");
5360 if (wireOp.use_empty())
5367 SmallPtrSet<Operation *, 4> usersReported;
5368 for (
auto *user : op->getUsers()) {
5369 if (!usersReported.insert(user).second)
5371 if (isa<ltl::LTLDialect, verif::VerifDialect>(user->getDialect()))
5373 if (isa<hw::WireOp>(user))
5375 auto d = op->emitError(
5376 "verification operation used in a non-verification context");
5377 d.attachNote(user->getLoc())
5378 <<
"leaking outside verification context here";
assert(baseType &&"element must be base type")
static void lowerModuleBody(FModuleOp mod, const DenseMap< StringAttr, PortConversion > &ports)
static Operation * buildImmediateVerifOp(ImplicitLocOpBuilder &builder, StringRef opName, Args &&...args)
Helper function to build an immediate assert operation based on the original FIRRTL operation name.
static Operation * buildConcurrentVerifOp(ImplicitLocOpBuilder &builder, StringRef opName, Args &&...args)
Helper function to build a concurrent assert operation based on the original FIRRTL operation name.
static unsigned getBitWidthFromVectorSize(unsigned size)
static void moveVerifAnno(ModuleOp top, AnnotationSet &annos, StringRef annoClass, StringRef attrBase)
Move a ExtractTestCode related annotation from annotations to an attribute.
static Value castToFIRRTLType(Value val, Type type, ImplicitLocOpBuilder &builder)
Cast a value to a desired target type.
static ArrayAttr getHWParameters(FExtModuleOp module, bool ignoreValues)
Map the parameter specifier on the specified extmodule into the HWModule representation for parameter...
static bool isZeroBitFIRRTLType(Type type)
Return true if the specified type is a sized FIRRTL type (Int or Analog) with zero bits.
static Value tryEliminatingAttachesToAnalogValue(Value value, Operation *insertPoint)
Given a value of analog type, check to see the only use of it is an attach.
static LogicalResult handleZeroBit(Value failedOperand, const std::function< LogicalResult()> &fn)
Zero bit operands end up looking like failures from getLoweredValue.
static const char moduleHierarchyFileAttrName[]
Attribute that indicates that the module hierarchy starting at the annotated module should be dumped ...
static void tryCopyName(Operation *dst, Operation *src)
static LogicalResult verifyOpLegality(Operation *op)
This verifies that the target operation has been lowered to a legal operation.
static Value castFromFIRRTLType(Value val, Type type, ImplicitLocOpBuilder &builder)
Cast from a FIRRTL type (potentially with a flip) to a standard type.
static SmallVector< SubfieldOp > getAllFieldAccesses(Value structValue, StringRef field)
static Value tryEliminatingConnectsToValue(Value flipValue, Operation *insertPoint, CircuitLoweringState &loweringState)
Given a value of flip type, check to see if all of the uses of it are connects.
static LogicalResult resolveFormatString(Location loc, StringRef originalFormatString, mlir::OperandRange operands, StringAttr &result)
static Value getSingleNonInstanceOperand(AttachOp op)
static IntType getWidestIntType(Type t1, Type t2)
Given two FIRRTL integer types, return the widest one.
static FailureOr< VectorizeOp > lowerBody(VectorizeOp op)
Vectorizes the body of the given arc.vectorize operation if it is not already vectorized.
static Location getLoc(DefSlot slot)
static Block * getBodyBlock(FModuleLike mod)
std::shared_ptr< calyx::CalyxLoweringState > loweringState
Instantiate one of these and use it to build typed backedges.
void abandon()
Abandon the backedges, suppressing any diagnostics if they are still active upon destruction of the b...
Backedge get(mlir::Type resultType, mlir::LocationAttr optionalLoc={})
Create a typed backedge.
mlir::LogicalResult clearOrEmitError()
Clear the backedges, erasing any remaining cursor ops.
Backedge is a wrapper class around a Value.
A namespace that is used to store existing names and generate new names in some scope within the IR.
This class provides a read-only projection over the MLIR attributes that represent a set of annotatio...
bool removeAnnotations(llvm::function_ref< bool(Annotation)> predicate)
Remove all annotations from this annotation set for which predicate returns true.
bool removeAnnotation(Annotation anno)
Remove an annotation from this annotation set.
Annotation getAnnotation(StringRef className) const
If this annotation set has an annotation with the specified class name, return it.
This class provides a read-only projection of an annotation.
DictionaryAttr getDict() const
Get the data dictionary of this attribute.
AttrClass getMember(StringAttr name) const
Return a member of the annotation.
bool isClass(Args... names) const
Return true if this annotation matches any of the specified class names.
FIRRTLVisitor allows you to visit all of the expr/stmt/decls with one class declaration.
ResultType visitInvalidOp(Operation *op, ExtraArgs... args)
visitInvalidOp is an override point for non-FIRRTL dialect operations.
ResultType visitUnhandledOp(Operation *op, ExtraArgs... args)
visitUnhandledOp is an override point for FIRRTL dialect ops that the concrete visitor didn't bother ...
This graph tracks modules and where they are instantiated.
FModuleLike getTopLevelModule()
Get the module corresponding to the top-level module of a circuit.
This is the common base class between SIntType and UIntType.
This table tracks nlas and what modules participate in them.
This is an edge in the InstanceGraph.
Direction get(bool isOutput)
Returns an output direction if isOutput is true, otherwise returns an input direction.
StringRef getFragmentsAttrName()
Return the name of the fragments array attribute.
constexpr const char * extractCoverageAnnoClass
constexpr const char * elaborationArtefactsDirectoryAnnoClass
FIRRTLBaseType getBaseType(Type type)
If it is a base type, return it as is.
constexpr const char * extractGrandCentralClass
constexpr const char * blackBoxAnnoClass
constexpr const char * dontObfuscateModuleAnnoClass
constexpr const char * metadataDirectoryAttrName
constexpr const char * testBenchDirAnnoClass
std::pair< hw::InnerSymAttr, StringAttr > getOrAddInnerSym(MLIRContext *context, hw::InnerSymAttr attr, uint64_t fieldID, llvm::function_ref< hw::InnerSymbolNamespace &()> getNamespace)
Ensure that the the InnerSymAttr has a symbol on the field specified.
constexpr const char * dutAnnoClass
constexpr const char * forceNameAnnoClass
constexpr const char * noDedupAnnoClass
bool hasDroppableName(Operation *op)
Return true if the name is droppable.
constexpr const char * extractAssertAnnoClass
constexpr const char * verifBlackBoxAnnoClass
constexpr const char * blackBoxTargetDirAnnoClass
Type lowerType(Type type, std::optional< Location > loc={}, llvm::function_ref< hw::TypeAliasType(Type, BaseTypeAliasType, Location)> getTypeDeclFn={})
Given a type, return the corresponding lowered type for the HW dialect.
bool isExpression(Operation *op)
Return true if the specified operation is a firrtl expression.
constexpr const char * extractAssumeAnnoClass
constexpr const char * testHarnessHierAnnoClass
constexpr const char * moduleHierAnnoClass
std::optional< int64_t > getBitWidth(FIRRTLBaseType type, bool ignoreFlip=false)
StringAttr getName(ArrayAttr names, size_t idx)
Return the name at the specified index of the ArrayAttr or null if it cannot be determined.
void setSVAttributes(mlir::Operation *op, mlir::ArrayAttr attrs)
Set the SV attributes of an operation.
The InstanceGraph op interface, see InstanceGraphInterface.td for more details.
std::unique_ptr< mlir::Pass > createLowerFIRRTLToHWPass(bool enableAnnotationWarning=false, firrtl::VerificationFlavor assertionFlavor=firrtl::VerificationFlavor::None)
This is the pass constructor.
int run(Type[Generator] generator=CppGenerator, cmdline_args=sys.argv)
reg(value, clock, reset=None, reset_value=None, name=None, sym_name=None)
This holds the name and type that describes the module's ports.
bool isOutput() const
Return true if this is a simple output-only port.
AnnotationSet annotations
bool isInput() const
Return true if this is a simple input-only port.
This holds the name, type, direction of a module's ports.
size_t argNum
This is the argument index or the result index depending on the direction.
void setSym(InnerSymAttr sym, MLIRContext *ctx)
InnerSymAttr getSym() const