36#include "mlir/IR/BuiltinOps.h"
37#include "mlir/IR/BuiltinTypes.h"
38#include "mlir/IR/ImplicitLocOpBuilder.h"
39#include "mlir/IR/Threading.h"
40#include "mlir/Pass/Pass.h"
41#include "llvm/ADT/DenseMap.h"
42#include "llvm/Support/Debug.h"
43#include "llvm/Support/Mutex.h"
44#include "llvm/Support/Path.h"
46#define DEBUG_TYPE "lower-to-hw"
49#define GEN_PASS_DEF_LOWERFIRRTLTOHW
50#include "circt/Conversion/Passes.h.inc"
54using namespace firrtl;
55using circt::comb::ICmpPredicate;
64 auto ftype = dyn_cast<FIRRTLBaseType>(type);
65 return ftype && ftype.getPassiveType().getBitWidthOrSentinel() == 0;
72 for (
auto operand : op.getAttached()) {
74 operand.getDefiningOp<InstanceOp>())
78 if (!operand.hasOneUse() || singleSource)
80 singleSource = operand;
89 auto checkTypes = [](Operation *op) -> WalkResult {
91 if (isa_and_nonnull<FIRRTLDialect>(op->getDialect()))
92 return op->emitError(
"Found unhandled FIRRTL operation '")
93 << op->getName() <<
"'";
96 auto checkTypeRange = [&](TypeRange types) -> LogicalResult {
97 if (llvm::any_of(types, [](Type type) {
98 return isa<FIRRTLDialect>(type.getDialect());
100 return op->emitOpError(
"found unhandled FIRRTL type");
105 if (failed(checkTypeRange(op->getOperandTypes())) ||
106 failed(checkTypeRange(op->getResultTypes())))
107 return WalkResult::interrupt();
110 for (
auto ®ion : op->getRegions())
111 for (
auto &block : region)
112 if (failed(checkTypeRange(block.getArgumentTypes())))
113 return WalkResult::interrupt();
116 return WalkResult::advance();
119 if (checkTypes(op).wasInterrupted() || op->walk(checkTypes).wasInterrupted())
126 auto t1c = type_cast<IntType>(t1), t2c = type_cast<IntType>(t2);
127 return t2c.getWidth() > t1c.getWidth() ? t2c : t1c;
133 ImplicitLocOpBuilder &builder) {
135 if (BundleType bundle = dyn_cast<BundleType>(type))
136 val = builder.createOrFold<HWStructCastOp>(bundle.getPassiveType(), val);
138 if (type != val.getType())
139 val = mlir::UnrealizedConversionCastOp::create(builder, type, val)
147 ImplicitLocOpBuilder &builder) {
149 if (hw::StructType structTy = dyn_cast<hw::StructType>(type)) {
151 val = mlir::UnrealizedConversionCastOp::create(
153 type_cast<FIRRTLBaseType>(val.getType()).getPassiveType(), val)
155 val = builder.createOrFold<HWStructCastOp>(type, val);
160 mlir::UnrealizedConversionCastOp::create(builder, type, val).getResult(0);
167 StringRef annoClass, StringRef attrBase) {
169 auto *ctx = top.getContext();
172 if (
auto dir = anno.getMember<StringAttr>(
"directory")) {
173 SmallVector<NamedAttribute> old;
174 for (
auto i : top->getAttrs())
177 StringAttr::get(ctx, attrBase),
178 hw::OutputFileAttr::getAsDirectory(ctx, dir.getValue(),
true,
true));
181 if (
auto file = anno.getMember<StringAttr>(
"filename")) {
182 SmallVector<NamedAttribute> old;
183 for (
auto i : top->getAttrs())
185 old.emplace_back(StringAttr::get(ctx, attrBase +
".bindfile"),
186 hw::OutputFileAttr::getFromFilename(
187 ctx, file.getValue(),
true));
193 return size == 1 ? 1 : llvm::Log2_64_Ceil(size);
199 if (
auto attr = src->getAttrOfType<StringAttr>(
"name"))
200 if (!dst->hasAttr(
"sv.namehint") && !dst->hasAttr(
"name"))
201 dst->setAttr(
"sv.namehint", attr);
207class FileDescriptorInfo {
209 FileDescriptorInfo(StringAttr outputFileName, mlir::ValueRange substitutions)
210 : outputFileFormat(outputFileName), substitutions(substitutions) {
212 substitutions.empty() &&
213 "substitutions must be empty when output file name is empty");
216 FileDescriptorInfo() =
default;
219 bool isSubstitutionRequired()
const {
return !substitutions.empty(); }
222 bool isDefaultFd()
const {
return !outputFileFormat; }
224 StringAttr getOutputFileFormat()
const {
return outputFileFormat; }
225 mlir::ValueRange getSubstitutions()
const {
return substitutions; }
229 StringAttr outputFileFormat = {};
232 mlir::ValueRange substitutions;
242struct FIRRTLModuleLowering;
245struct CircuitLoweringState {
247 std::atomic<bool> usedPrintf{
false};
248 std::atomic<bool> usedAssertVerboseCond{
false};
249 std::atomic<bool> usedStopCond{
false};
250 std::atomic<bool> usedFileDescriptorLib{
false};
252 CircuitLoweringState(CircuitOp circuitOp,
bool enableAnnotationWarning,
255 : circuitOp(circuitOp), instanceGraph(instanceGraph),
256 enableAnnotationWarning(enableAnnotationWarning),
257 verificationFlavor(verificationFlavor), nlaTable(nlaTable) {
258 auto *
context = circuitOp.getContext();
263 auto dirName = tbAnno.getMember<StringAttr>(
"dirname");
264 testBenchDirectory = hw::OutputFileAttr::getAsDirectory(
265 context, dirName.getValue(),
false,
true);
269 if (
auto module = dyn_cast<FModuleLike>(op))
280 testHarness =
nullptr;
281 }
else if (dut == testHarness) {
282 testHarness =
nullptr;
287 auto inDUT = [&](igraph::ModuleOpInterface child) {
289 if (
auto inst = instRec->getInstance<InstanceOp>())
290 return inst.getLowerToBind() || inst.getDoNotPrint();
293 if (
auto parent = dyn_cast<igraph::ModuleOpInterface>(*dut))
294 return getInstanceGraph().isAncestor(child, parent, isPhony);
297 circuitOp->walk([&](FModuleLike moduleOp) {
299 dutModules.insert(moduleOp);
303 Operation *getNewModule(Operation *oldModule) {
304 auto it = oldToNewModuleMap.find(oldModule);
305 return it != oldToNewModuleMap.end() ? it->second :
nullptr;
308 Operation *getOldModule(Operation *newModule) {
309 auto it = newToOldModuleMap.find(newModule);
310 return it != newToOldModuleMap.end() ? it->second :
nullptr;
313 void recordModuleMapping(Operation *oldFMod, Operation *newHWMod) {
314 oldToNewModuleMap[oldFMod] = newHWMod;
315 newToOldModuleMap[newHWMod] = oldFMod;
320 void processRemainingAnnotations(Operation *op,
const AnnotationSet &annoSet);
326 void addBind(sv::BindOp op) {
327 std::lock_guard<std::mutex> lock(bindsMutex);
333 hw::TypeAliasType getTypeAlias(Type rawType, BaseTypeAliasType firAliasType,
336 auto hwAlias = typeAliases.getTypedecl(firAliasType);
339 assert(!typeAliases.isFrozen() &&
340 "type aliases cannot be generated after its frozen");
341 return typeAliases.addTypedecl(rawType, firAliasType, typeLoc);
344 FModuleLike getDut() {
return dut; }
345 FModuleLike getTestHarness() {
return testHarness; }
351 bool isInDUT(igraph::ModuleOpInterface child) {
352 if (
auto hwModule = dyn_cast<hw::HWModuleOp>(child.getOperation()))
353 child = cast<igraph::ModuleOpInterface>(getOldModule(hwModule));
354 return dutModules.contains(child);
357 hw::OutputFileAttr getTestBenchDirectory() {
return testBenchDirectory; }
362 bool isInTestHarness(igraph::ModuleOpInterface mod) {
return !isInDUT(mod); }
369 Type
lowerType(Type type, Location loc) {
370 return ::lowerType(type, loc,
371 [&](Type rawType, BaseTypeAliasType firrtlType,
372 Location typeLoc) -> hw::TypeAliasType {
373 return getTypeAlias(rawType, firrtlType, typeLoc);
379 llvm::sys::SmartScopedLock<true> lock(verbatimSourcesMutex);
380 auto it = verbatimSourcesByFileName.find(fileName);
381 return it != verbatimSourcesByFileName.end() ? it->second :
nullptr;
386 void registerVerbatimSource(StringRef fileName,
388 llvm::sys::SmartScopedLock<true> lock(verbatimSourcesMutex);
389 verbatimSourcesByFileName[fileName] = verbatimOp;
393 emit::FileOp getEmitFileForFile(StringRef fileName) {
394 llvm::sys::SmartScopedLock<true> lock(emitFilesMutex);
395 auto it = emitFilesByFileName.find(fileName);
396 return it != emitFilesByFileName.end() ? it->second :
nullptr;
401 void registerEmitFile(StringRef fileName, emit::FileOp fileOp) {
402 llvm::sys::SmartScopedLock<true> lock(emitFilesMutex);
403 emitFilesByFileName[fileName] = fileOp;
407 friend struct FIRRTLModuleLowering;
408 friend struct FIRRTLLowering;
409 CircuitLoweringState(
const CircuitLoweringState &) =
delete;
410 void operator=(
const CircuitLoweringState &) =
delete;
413 DenseMap<Operation *, Operation *> oldToNewModuleMap;
416 DenseMap<Operation *, Operation *> newToOldModuleMap;
427 DenseSet<igraph::ModuleOpInterface> dutModules;
431 StringSet<> pendingAnnotations;
432 const bool enableAnnotationWarning;
433 std::mutex annotationPrintingMtx;
439 SmallVector<sv::BindOp> binds;
442 std::mutex bindsMutex;
450 FModuleLike testHarness;
453 hw::OutputFileAttr testBenchDirectory;
457 DenseMap<std::pair<Attribute, Attribute>, Attribute> instanceForceNames;
460 SetVector<StringAttr> macroDeclNames;
461 std::mutex macroDeclMutex;
463 void addMacroDecl(StringAttr name) {
464 std::unique_lock<std::mutex> lock(macroDeclMutex);
465 macroDeclNames.insert(name);
470 DenseMap<hw::HWModuleOp, SetVector<Attribute>> fragments;
471 llvm::sys::SmartMutex<true> fragmentsMutex;
474 llvm::sys::SmartScopedLock<true> lock(fragmentsMutex);
475 fragments[module].insert(
476 FlatSymbolRefAttr::get(circuitOp.getContext(), fragment));
491 struct RecordTypeAlias {
493 RecordTypeAlias(CircuitOp c) : circuitOp(c) {}
495 hw::TypeAliasType getTypedecl(BaseTypeAliasType firAlias)
const {
496 auto iter = firrtlTypeToAliasTypeMap.find(firAlias);
497 if (iter != firrtlTypeToAliasTypeMap.end())
502 bool isFrozen() {
return frozen; }
504 void freeze() { frozen =
true; }
506 hw::TypeAliasType addTypedecl(Type rawType, BaseTypeAliasType firAlias,
508 assert(!frozen &&
"Record already frozen, cannot be updated");
511 auto b = ImplicitLocOpBuilder::atBlockBegin(
513 &circuitOp->getParentRegion()->getBlocks().back());
515 b, b.getStringAttr(circuitOp.getName() +
"__TYPESCOPE_"));
516 typeScope.getBodyRegion().push_back(
new Block());
518 auto typeName = firAlias.getName();
523 StringAttr::get(typeName.getContext(),
524 typeDeclNamespace.newName(typeName.getValue()));
526 auto typeScopeBuilder =
527 ImplicitLocOpBuilder::atBlockEnd(typeLoc, typeScope.getBodyBlock());
529 typeName, rawType,
nullptr);
530 auto hwAlias = hw::TypeAliasType::get(
531 SymbolRefAttr::get(typeScope.getSymNameAttr(),
532 {FlatSymbolRefAttr::get(typeDecl)}),
534 auto insert = firrtlTypeToAliasTypeMap.try_emplace(firAlias, hwAlias);
535 assert(insert.second &&
"Entry already exists, insert failed");
536 return insert.first->second;
545 DenseMap<Type, hw::TypeAliasType> firrtlTypeToAliasTypeMap;
553 RecordTypeAlias typeAliases = RecordTypeAlias(circuitOp);
556 llvm::StringMap<sv::SVVerbatimSourceOp> verbatimSourcesByFileName;
557 llvm::sys::SmartMutex<true> verbatimSourcesMutex;
560 llvm::StringMap<emit::FileOp> emitFilesByFileName;
561 llvm::sys::SmartMutex<true> emitFilesMutex;
564void CircuitLoweringState::processRemainingAnnotations(
566 if (!enableAnnotationWarning || annoSet.
empty())
568 std::lock_guard<std::mutex> lock(annotationPrintingMtx);
570 for (
auto a : annoSet) {
571 auto inserted = pendingAnnotations.insert(a.getClass());
572 if (!inserted.second)
611 mlir::emitWarning(op->getLoc(),
"unprocessed annotation:'" + a.getClass() +
612 "' still remaining after LowerToHW");
618struct FIRRTLModuleLowering
619 :
public circt::impl::LowerFIRRTLToHWBase<FIRRTLModuleLowering> {
621 void runOnOperation()
override;
622 void setEnableAnnotationWarning() { enableAnnotationWarning =
true; }
624 using LowerFIRRTLToHWBase<FIRRTLModuleLowering>::verificationFlavor;
627 void lowerFileHeader(CircuitOp op, CircuitLoweringState &
loweringState);
628 LogicalResult lowerPorts(ArrayRef<PortInfo> firrtlPorts,
629 SmallVectorImpl<hw::PortInfo> &ports,
630 Operation *moduleOp, StringRef moduleName,
632 bool handleForceNameAnnos(FModuleLike oldModule,
AnnotationSet &annos,
634 hw::HWModuleOp lowerModule(FModuleOp oldModule, Block *topLevelModule,
637 getVerbatimSourceForExtModule(FExtModuleOp oldModule, Block *topLevelModule,
639 hw::HWModuleLike lowerExtModule(FExtModuleOp oldModule, Block *topLevelModule,
642 lowerVerbatimExtModule(FExtModuleOp oldModule, Block *topLevelModule,
645 Block *topLevelModule,
649 lowerModulePortsAndMoveBody(FModuleOp oldModule,
hw::HWModuleOp newModule,
653 LogicalResult lowerFormalBody(verif::FormalOp formalOp,
655 LogicalResult lowerSimulationBody(verif::SimulationOp simulationOp,
657 LogicalResult lowerFileBody(emit::FileOp op);
665 bool enableAnnotationWarning,
667 auto pass = std::make_unique<FIRRTLModuleLowering>();
668 if (enableAnnotationWarning)
669 pass->setEnableAnnotationWarning();
670 pass->verificationFlavor = verificationFlavor;
676void FIRRTLModuleLowering::runOnOperation() {
680 auto *topLevelModule = getOperation().getBody();
684 for (
auto &op : *topLevelModule) {
685 if ((circuit = dyn_cast<CircuitOp>(&op)))
692 auto *circuitBody = circuit.getBodyBlock();
696 CircuitLoweringState state(circuit, enableAnnotationWarning,
697 verificationFlavor, getAnalysis<InstanceGraph>(),
698 &getAnalysis<NLATable>());
700 SmallVector<Operation *, 32> opsToProcess;
704 "firrtl.extract.assert");
706 "firrtl.extract.assume");
708 "firrtl.extract.cover");
709 circuitAnno.removeAnnotationsWithClass(
712 state.processRemainingAnnotations(circuit, circuitAnno);
715 for (
auto &op : make_early_inc_range(circuitBody->getOperations())) {
717 TypeSwitch<Operation *, LogicalResult>(&op)
718 .Case<FModuleOp>([&](
auto module) {
719 auto loweredMod = lowerModule(module, topLevelModule, state);
723 state.recordModuleMapping(&op, loweredMod);
724 opsToProcess.push_back(loweredMod);
726 module.walk([&](Operation *op) {
727 for (auto res : op->getResults()) {
729 type_dyn_cast<BaseTypeAliasType>(res.getType()))
730 state.lowerType(aliasType, op->getLoc());
733 return lowerModulePortsAndMoveBody(module, loweredMod, state);
735 .Case<FExtModuleOp>([&](
auto extModule) {
737 lowerExtModule(extModule, topLevelModule, state);
740 state.recordModuleMapping(&op, loweredMod);
743 .Case<FMemModuleOp>([&](
auto memModule) {
745 lowerMemModule(memModule, topLevelModule, state);
748 state.recordModuleMapping(&op, loweredMod);
751 .Case<FormalOp>([&](
auto oldOp) {
752 auto builder = OpBuilder::atBlockEnd(topLevelModule);
753 auto newOp = verif::FormalOp::create(builder, oldOp.getLoc(),
755 oldOp.getParametersAttr());
756 newOp.getBody().emplaceBlock();
757 state.recordModuleMapping(oldOp, newOp);
758 opsToProcess.push_back(newOp);
761 .Case<SimulationOp>([&](
auto oldOp) {
762 auto loc = oldOp.getLoc();
763 auto builder = OpBuilder::atBlockEnd(topLevelModule);
764 auto newOp = verif::SimulationOp::create(
765 builder, loc, oldOp.getNameAttr(), oldOp.getParametersAttr());
766 auto &body = newOp.getRegion().emplaceBlock();
767 body.addArgument(seq::ClockType::get(builder.getContext()), loc);
768 body.addArgument(builder.getI1Type(), loc);
769 state.recordModuleMapping(oldOp, newOp);
770 opsToProcess.push_back(newOp);
773 .Case<emit::FileOp>([&](
auto fileOp) {
774 fileOp->moveBefore(topLevelModule, topLevelModule->end());
775 opsToProcess.push_back(fileOp);
778 .Default([&](Operation *op) {
783 op->moveBefore(topLevelModule, topLevelModule->end());
789 return signalPassFailure();
792 state.typeAliases.freeze();
797 SmallVector<Attribute> dutHierarchyFiles;
798 SmallVector<Attribute> testHarnessHierarchyFiles;
799 circuitAnno.removeAnnotations([&](
Annotation annotation) {
801 auto file = hw::OutputFileAttr::getFromFilename(
803 annotation.
getMember<StringAttr>(
"filename").getValue(),
805 dutHierarchyFiles.push_back(file);
809 auto file = hw::OutputFileAttr::getFromFilename(
811 annotation.
getMember<StringAttr>(
"filename").getValue(),
815 if (state.getTestHarness())
816 testHarnessHierarchyFiles.push_back(file);
818 dutHierarchyFiles.push_back(file);
824 if (!dutHierarchyFiles.empty())
825 state.getNewModule(state.getDut())
827 ArrayAttr::get(&getContext(), dutHierarchyFiles));
828 if (!testHarnessHierarchyFiles.empty())
829 state.getNewModule(state.getTestHarness())
831 ArrayAttr::get(&getContext(), testHarnessHierarchyFiles));
835 mlir::failableParallelForEach(&getContext(), opsToProcess, [&](
auto op) {
839 return signalPassFailure();
842 for (
auto bind : state.binds) {
847 for (
auto &[module, fragments] : state.fragments)
849 ArrayAttr::
get(&getContext(), fragments.getArrayRef()));
852 for (
auto oldNew : state.oldToNewModuleMap)
853 oldNew.first->erase();
855 if (!state.macroDeclNames.empty()) {
856 ImplicitLocOpBuilder b(UnknownLoc::get(&getContext()), circuit);
857 for (
auto name : state.macroDeclNames) {
858 sv::MacroDeclOp::create(b, name);
863 lowerFileHeader(circuit, state);
870void FIRRTLModuleLowering::lowerFileHeader(CircuitOp op,
871 CircuitLoweringState &state) {
874 ImplicitLocOpBuilder b(UnknownLoc::get(&getContext()), op);
878 auto emitGuardedDefine = [&](StringRef guard, StringRef defName,
879 StringRef defineTrue =
"",
880 StringRef defineFalse = StringRef()) {
881 if (!defineFalse.data()) {
882 assert(defineTrue.data() &&
"didn't define anything");
884 b, guard, [&]() { sv::MacroDefOp::create(b, defName, defineTrue); });
889 if (defineTrue.data())
890 sv::MacroDefOp::create(b, defName, defineTrue);
892 [&]() { sv::MacroDefOp::create(b, defName, defineFalse); });
897 auto emitGuard = [&](
const char *guard, llvm::function_ref<void(
void)> body) {
899 b, guard, [] {}, body);
902 if (state.usedFileDescriptorLib) {
904 SmallVector<hw::ModulePort> ports;
908 namePort.
name = b.getStringAttr(
"name");
909 namePort.
type = hw::StringType::get(b.getContext());
910 namePort.
dir = hw::ModulePort::Direction::Input;
911 ports.push_back(namePort);
915 fdPort.
name = b.getStringAttr(
"fd");
916 fdPort.
type = b.getIntegerType(32);
917 fdPort.
dir = hw::ModulePort::Direction::Output;
918 ports.push_back(fdPort);
921 auto moduleType = hw::ModuleType::get(b.getContext(), ports);
923 SmallVector<NamedAttribute> perArgumentsAttr;
924 perArgumentsAttr.push_back(
925 {sv::FuncOp::getExplicitlyReturnedAttrName(), b.getUnitAttr()});
927 SmallVector<Attribute> argumentAttr = {
928 DictionaryAttr::get(b.getContext(), {}),
929 DictionaryAttr::get(b.getContext(), perArgumentsAttr)};
932 auto func = sv::FuncOp::create(
934 "__circt_lib_logging::FileDescriptor::get", moduleType,
937 {b.getDictionaryAttr({}), b.getDictionaryAttr(perArgumentsAttr)}),
943 b.getStringAttr(
"__circt_lib_logging::FileDescriptor::get"));
946 sv::MacroDeclOp::create(b,
"__CIRCT_LIB_LOGGING");
948 emit::FragmentOp::create(b,
"CIRCT_LIB_LOGGING_FRAGMENT", [&] {
949 emitGuard(
"SYNTHESIS", [&]() {
950 emitGuard(
"__CIRCT_LIB_LOGGING", [&]() {
951 sv::VerbatimOp::create(b, R
"(// CIRCT Logging Library
952package __circt_lib_logging;
953 class FileDescriptor;
954 static int global_id [string];
955 static function int get(string name);
956 if (global_id.exists(name) == 32'h0) begin
957 global_id[name] = $fopen(name, "w");
958 if (global_id[name] == 32'h0)
959 $error("Failed to open file %s", name);
961 return global_id[name];
967 sv::MacroDefOp::create(b, "__CIRCT_LIB_LOGGING",
"");
973 if (state.usedPrintf) {
974 sv::MacroDeclOp::create(b,
"PRINTF_COND");
975 sv::MacroDeclOp::create(b,
"PRINTF_COND_");
976 emit::FragmentOp::create(b,
"PRINTF_COND_FRAGMENT", [&] {
977 sv::VerbatimOp::create(
978 b,
"\n// Users can define 'PRINTF_COND' to add an extra gate to "
980 emitGuard(
"PRINTF_COND_", [&]() {
981 emitGuardedDefine(
"PRINTF_COND",
"PRINTF_COND_",
"(`PRINTF_COND)",
"1");
986 if (state.usedAssertVerboseCond) {
987 sv::MacroDeclOp::create(b,
"ASSERT_VERBOSE_COND");
988 sv::MacroDeclOp::create(b,
"ASSERT_VERBOSE_COND_");
989 emit::FragmentOp::create(b,
"ASSERT_VERBOSE_COND_FRAGMENT", [&] {
990 sv::VerbatimOp::create(
991 b,
"\n// Users can define 'ASSERT_VERBOSE_COND' to add an extra "
992 "gate to assert error printing.");
993 emitGuard(
"ASSERT_VERBOSE_COND_", [&]() {
994 emitGuardedDefine(
"ASSERT_VERBOSE_COND",
"ASSERT_VERBOSE_COND_",
995 "(`ASSERT_VERBOSE_COND)",
"1");
1000 if (state.usedStopCond) {
1001 sv::MacroDeclOp::create(b,
"STOP_COND");
1002 sv::MacroDeclOp::create(b,
"STOP_COND_");
1003 emit::FragmentOp::create(b,
"STOP_COND_FRAGMENT", [&] {
1004 sv::VerbatimOp::create(
1005 b,
"\n// Users can define 'STOP_COND' to add an extra gate "
1006 "to stop conditions.");
1007 emitGuard(
"STOP_COND_", [&]() {
1008 emitGuardedDefine(
"STOP_COND",
"STOP_COND_",
"(`STOP_COND)",
"1");
1015FIRRTLModuleLowering::lowerPorts(ArrayRef<PortInfo> firrtlPorts,
1016 SmallVectorImpl<hw::PortInfo> &ports,
1017 Operation *moduleOp, StringRef moduleName,
1019 ports.reserve(firrtlPorts.size());
1021 size_t numResults = 0;
1022 for (
auto e :
llvm::enumerate(firrtlPorts)) {
1024 size_t portNo = e.index();
1029 if (firrtlPort.
sym.size() > 1 ||
1030 (firrtlPort.
sym.size() == 1 && !firrtlPort.
sym.getSymName()))
1031 return emitError(firrtlPort.
loc)
1032 <<
"cannot lower aggregate port " << firrtlPort.
name
1033 <<
" with field sensitive symbols, HW dialect does not support "
1034 "per field symbols yet.";
1035 hwPort.
setSym(firrtlPort.
sym, moduleOp->getContext());
1037 if (hadDontTouch && !hwPort.
getSym()) {
1038 if (hwPort.
type.isInteger(0)) {
1039 if (enableAnnotationWarning) {
1040 mlir::emitWarning(firrtlPort.
loc)
1041 <<
"zero width port " << hwPort.
name
1042 <<
" has dontTouch annotation, removing anyway";
1048 hw::InnerSymAttr::get(StringAttr::get(
1049 moduleOp->getContext(),
1050 Twine(
"__") + moduleName + Twine(
"__DONTTOUCH__") +
1051 Twine(portNo) + Twine(
"__") + firrtlPort.
name.strref())),
1052 moduleOp->getContext());
1057 moduleOp->emitError(
"cannot lower this port type to HW");
1063 if (hwPort.
type.isInteger(0)) {
1064 auto sym = hwPort.
getSym();
1065 if (sym && !sym.empty()) {
1066 return mlir::emitError(firrtlPort.
loc)
1067 <<
"zero width port " << hwPort.
name
1068 <<
" is referenced by name [" << sym
1069 <<
"] (e.g. in an XMR) but must be removed";
1076 hwPort.
dir = hw::ModulePort::Direction::Output;
1077 hwPort.
argNum = numResults++;
1078 }
else if (firrtlPort.
isInput()) {
1079 hwPort.
dir = hw::ModulePort::Direction::Input;
1080 hwPort.
argNum = numArgs++;
1084 hwPort.
type = hw::InOutType::get(hwPort.
type);
1085 hwPort.
dir = hw::ModulePort::Direction::InOut;
1086 hwPort.
argNum = numArgs++;
1088 hwPort.
loc = firrtlPort.
loc;
1089 ports.push_back(hwPort);
1099 auto params = llvm::map_range(module.getParameters(), [](Attribute a) {
1100 return cast<ParamDeclAttr>(a);
1105 Builder builder(module);
1110 SmallVector<Attribute> newParams;
1111 for (
const ParamDeclAttr &entry : params) {
1112 auto name = entry.getName();
1113 auto type = entry.getType();
1114 auto value = ignoreValues ? Attribute() : entry.getValue();
1116 hw::ParamDeclAttr::get(builder.getContext(), name, type, value);
1117 newParams.push_back(paramAttr);
1119 return builder.getArrayAttr(newParams);
1122bool FIRRTLModuleLowering::handleForceNameAnnos(
1125 bool failed =
false;
1131 auto sym = anno.
getMember<FlatSymbolRefAttr>(
"circt.nonlocal");
1138 auto diag = oldModule.emitOpError()
1140 <<
"' that is not a non-local annotation";
1141 diag.attachNote() <<
"the erroneous annotation is '" << anno.
getDict()
1152 auto diag = oldModule.emitOpError()
1154 <<
"' whose non-local symbol, '" << sym
1155 <<
"' does not exist in the circuit";
1156 diag.attachNote() <<
"the erroneous annotation is '" << anno.
getDict();
1169 cast<hw::InnerRefAttr>(nla.getNamepath().getValue().take_back(2)[0]);
1171 {{inst.getModule(), inst.getName()}, anno.
getMember(
"name")});
1172 if (!inserted.second &&
1173 (anno.
getMember(
"name") != (inserted.first->second))) {
1174 auto diag = oldModule.emitError()
1176 <<
"' with different names: " << inserted.first->second
1177 <<
" was not " << anno.
getMember(
"name");
1178 diag.attachNote() <<
"the erroneous annotation is '" << anno.
getDict()
1189 FExtModuleOp oldModule, Block *topLevelModule,
1200 SmallVector<PortInfo> firrtlPorts = oldModule.getPorts();
1201 SmallVector<hw::PortInfo, 8> ports;
1202 if (failed(lowerPorts(firrtlPorts, ports, oldModule, oldModule.getName(),
1207 StringRef verilogName;
1208 if (
auto defName = oldModule.getDefname())
1209 verilogName = defName.value();
1211 verilogName = oldModule.getName();
1213 auto builder = OpBuilder::atBlockEnd(topLevelModule);
1215 auto filesAttr = verbatimAnno.
getMember<ArrayAttr>(
"files");
1216 if (!filesAttr || filesAttr.empty()) {
1217 oldModule->emitError(
"VerbatimBlackBoxAnno missing or empty files array");
1222 auto primaryFile = cast<DictionaryAttr>(filesAttr[0]);
1223 auto primaryFileContent = primaryFile.getAs<StringAttr>(
"content");
1224 auto primaryOutputFile = primaryFile.getAs<StringAttr>(
"output_file");
1226 if (!primaryFileContent || !primaryOutputFile) {
1227 oldModule->emitError(
"VerbatimBlackBoxAnno file missing fields");
1231 auto primaryOutputFileAttr = hw::OutputFileAttr::getFromFilename(
1232 builder.getContext(), primaryOutputFile.getValue());
1234 auto primaryFileName = llvm::sys::path::filename(primaryOutputFile);
1235 auto verbatimSource =
loweringState.getVerbatimSourceForFile(primaryFileName);
1238 SmallVector<Attribute> additionalFiles;
1242 for (
size_t i = 1; i < filesAttr.size(); ++i) {
1243 auto file = cast<DictionaryAttr>(filesAttr[i]);
1244 auto content = file.getAs<StringAttr>(
"content");
1245 auto outputFile = file.getAs<StringAttr>(
"output_file");
1246 auto fileName = llvm::sys::path::filename(outputFile);
1248 if (!(content && outputFile)) {
1249 oldModule->emitError(
"VerbatimBlackBoxAnno file missing fields");
1257 auto fileSymbolName = circuitNamespace.newName(fileName);
1258 emitFile = emit::FileOp::create(builder, oldModule.getLoc(),
1259 outputFile.getValue(), fileSymbolName);
1260 builder.setInsertionPointToStart(&
emitFile.getBodyRegion().front());
1261 emit::VerbatimOp::create(builder, oldModule.getLoc(), content);
1262 builder.setInsertionPointAfter(
emitFile);
1265 auto ext = llvm::sys::path::extension(outputFile.getValue());
1266 bool excludeFromFileList = (ext ==
".h" || ext ==
".vh" || ext ==
".svh");
1267 auto outputFileAttr = hw::OutputFileAttr::getFromFilename(
1268 builder.getContext(), outputFile.getValue(), excludeFromFileList);
1269 emitFile->setAttr(
"output_file", outputFileAttr);
1273 additionalFiles.push_back(FlatSymbolRefAttr::get(
emitFile));
1279 parameters = builder.getArrayAttr({});
1281 if (!verbatimSource) {
1282 verbatimSource = sv::SVVerbatimSourceOp::create(
1283 builder, oldModule.getLoc(),
1284 circuitNamespace.newName(primaryFileName.str()),
1285 primaryFileContent.getValue(), primaryOutputFileAttr, parameters,
1286 additionalFiles.empty() ?
nullptr
1287 : builder.getArrayAttr(additionalFiles),
1288 builder.getStringAttr(verilogName));
1290 SymbolTable::setSymbolVisibility(
1291 verbatimSource, SymbolTable::getSymbolVisibility(oldModule));
1293 loweringState.registerVerbatimSource(primaryFileName, verbatimSource);
1296 return verbatimSource;
1300FIRRTLModuleLowering::lowerExtModule(FExtModuleOp oldModule,
1301 Block *topLevelModule,
1303 if (
auto verbatimMod =
1304 lowerVerbatimExtModule(oldModule, topLevelModule,
loweringState))
1310 SmallVector<PortInfo> firrtlPorts = oldModule.getPorts();
1311 SmallVector<hw::PortInfo, 8> ports;
1312 if (failed(lowerPorts(firrtlPorts, ports, oldModule, oldModule.getName(),
1316 StringRef verilogName;
1317 if (
auto defName = oldModule.getDefname())
1318 verilogName = defName.value();
1321 auto builder = OpBuilder::atBlockEnd(topLevelModule);
1322 auto nameAttr = builder.getStringAttr(oldModule.getName());
1327 auto newModule = hw::HWModuleExternOp::create(
1328 builder, oldModule.getLoc(), nameAttr, ports, verilogName, parameters);
1329 SymbolTable::setSymbolVisibility(newModule,
1330 SymbolTable::getSymbolVisibility(oldModule));
1332 bool hasOutputPort =
1333 llvm::any_of(firrtlPorts, [&](
auto p) {
return p.isOutput(); });
1334 if (!hasOutputPort &&
1337 newModule->setAttr(
"firrtl.extract.cover.extra", builder.getUnitAttr());
1340 if (
auto extReqs = oldModule.getExternalRequirements();
1341 extReqs && !extReqs.empty())
1342 newModule->setAttr(
"circt.external_requirements", extReqs);
1347 loweringState.processRemainingAnnotations(oldModule, annos);
1352 FExtModuleOp oldModule, Block *topLevelModule,
1357 auto verbatimSource =
1358 getVerbatimSourceForExtModule(oldModule, topLevelModule,
loweringState);
1360 if (!verbatimSource)
1363 SmallVector<PortInfo> firrtlPorts = oldModule.getPorts();
1364 SmallVector<hw::PortInfo, 8> ports;
1365 if (failed(lowerPorts(firrtlPorts, ports, oldModule, oldModule.getName(),
1369 StringRef verilogName;
1370 if (
auto defName = oldModule.getDefname())
1371 verilogName = defName.value();
1373 auto builder = OpBuilder::atBlockEnd(topLevelModule);
1375 auto newModule = sv::SVVerbatimModuleOp::create(
1378 builder.getStringAttr(oldModule.getName()),
1380 FlatSymbolRefAttr::get(verbatimSource),
1381 parameters ? parameters : builder.getArrayAttr({}),
1382 verilogName.empty() ? StringAttr{}
1383 : builder.getStringAttr(verilogName));
1385 SymbolTable::setSymbolVisibility(newModule,
1386 SymbolTable::getSymbolVisibility(oldModule));
1388 bool hasOutputPort =
1389 llvm::any_of(firrtlPorts, [&](
auto p) {
return p.isOutput(); });
1390 if (!hasOutputPort &&
1393 newModule->setAttr(
"firrtl.extract.cover.extra", builder.getUnitAttr());
1396 if (
auto extReqs = oldModule.getExternalRequirements();
1397 extReqs && !extReqs.empty())
1398 newModule->setAttr(
"circt.external_requirements", extReqs);
1403 loweringState.processRemainingAnnotations(oldModule, annos);
1408FIRRTLModuleLowering::lowerMemModule(FMemModuleOp oldModule,
1409 Block *topLevelModule,
1412 SmallVector<PortInfo> firrtlPorts = oldModule.getPorts();
1413 SmallVector<hw::PortInfo, 8> ports;
1414 if (failed(lowerPorts(firrtlPorts, ports, oldModule, oldModule.getName(),
1419 auto builder = OpBuilder::atBlockEnd(topLevelModule);
1420 auto newModule = hw::HWModuleExternOp::create(
1421 builder, oldModule.getLoc(), oldModule.getModuleNameAttr(), ports,
1422 oldModule.getModuleNameAttr());
1430FIRRTLModuleLowering::lowerModule(FModuleOp oldModule, Block *topLevelModule,
1433 SmallVector<PortInfo> firrtlPorts = oldModule.getPorts();
1434 SmallVector<hw::PortInfo, 8> ports;
1435 if (failed(lowerPorts(firrtlPorts, ports, oldModule, oldModule.getName(),
1440 auto builder = OpBuilder::atBlockEnd(topLevelModule);
1441 auto nameAttr = builder.getStringAttr(oldModule.getName());
1443 hw::HWModuleOp::create(builder, oldModule.getLoc(), nameAttr, ports);
1445 if (
auto comment = oldModule->getAttrOfType<StringAttr>(
"comment"))
1446 newModule.setCommentAttr(comment);
1449 SmallVector<StringRef, 13> attrNames = {
1450 "annotations",
"convention",
"layers",
1451 "portNames",
"sym_name",
"portDirections",
1452 "portTypes",
"portAnnotations",
"portSymbols",
1453 "portLocations",
"parameters", SymbolTable::getVisibilityAttrName(),
1456 DenseSet<StringRef> attrSet(attrNames.begin(), attrNames.end());
1457 SmallVector<NamedAttribute> newAttrs(newModule->getAttrs());
1459 llvm::make_filter_range(oldModule->getAttrs(), [&](auto namedAttr) {
1460 return !attrSet.count(namedAttr.getName()) &&
1461 !newModule->getAttrDictionary().contains(namedAttr.getName());
1463 newAttrs.push_back(i);
1465 newModule->setAttrs(newAttrs);
1469 SymbolTable::setSymbolVisibility(newModule,
1470 SymbolTable::getSymbolVisibility(oldModule));
1476 newModule->setAttr(
"firrtl.extract.cover.extra", builder.getUnitAttr());
1480 if (
auto testBenchDir =
loweringState.getTestBenchDirectory())
1482 if (!newModule->hasAttr(
"output_file"))
1483 newModule->setAttr(
"output_file", testBenchDir);
1484 newModule->setAttr(
"firrtl.extract.do_not_extract",
1485 builder.getUnitAttr());
1486 newModule.setCommentAttr(
1487 builder.getStringAttr(
"VCS coverage exclude_file"));
1493 loweringState.processRemainingAnnotations(oldModule, annos);
1502 Operation *insertPoint) {
1503 if (!value.hasOneUse())
1506 auto attach = dyn_cast<AttachOp>(*value.user_begin());
1507 if (!attach || attach.getNumOperands() != 2)
1511 auto loweredType =
lowerType(value.getType());
1512 if (loweredType.isInteger(0))
1517 auto attachedValue = attach.getOperand(attach.getOperand(0) == value);
1518 auto *op = attachedValue.getDefiningOp();
1519 if (op && op->getBlock() == insertPoint->getBlock() &&
1520 !op->isBeforeInBlock(insertPoint))
1525 ImplicitLocOpBuilder builder(insertPoint->getLoc(), insertPoint);
1541 if (type_isa<AnalogType>(flipValue.getType()))
1544 Operation *connectOp =
nullptr;
1545 for (
auto &use : flipValue.getUses()) {
1548 if (use.getOperandNumber() != 0)
1550 if (!isa<ConnectOp, MatchingConnectOp>(use.getOwner()))
1556 connectOp = use.getOwner();
1566 loweringState.lowerType(flipValue.getType(), flipValue.getLoc());
1567 if (loweredType.isInteger(0))
1572 ImplicitLocOpBuilder builder(insertPoint->getLoc(), insertPoint);
1574 auto connectSrc = connectOp->getOperand(1);
1577 if (!isa<FIRRTLType>(connectSrc.getType())) {
1583 if (!type_cast<FIRRTLBaseType>(connectSrc.getType()).isPassive())
1585 mlir::UnrealizedConversionCastOp::create(
1587 type_cast<FIRRTLBaseType>(connectSrc.getType()).getPassiveType(),
1593 auto destTy = type_cast<FIRRTLBaseType>(flipValue.getType()).getPassiveType();
1595 if (destTy != connectSrc.getType() &&
1596 (isa<BaseTypeAliasType>(connectSrc.getType()) ||
1597 isa<BaseTypeAliasType>(destTy))) {
1599 builder.createOrFold<BitCastOp>(flipValue.getType(), connectSrc);
1601 if (!destTy.isGround()) {
1603 if (destTy != type_cast<FIRRTLType>(connectSrc.getType()))
1605 }
else if (destTy.getBitWidthOrSentinel() !=
1606 type_cast<FIRRTLBaseType>(connectSrc.getType())
1607 .getBitWidthOrSentinel()) {
1610 auto destWidth = destTy.getBitWidthOrSentinel();
1611 assert(destWidth != -1 &&
"must know integer widths");
1612 connectSrc = builder.createOrFold<PadPrimOp>(destTy, connectSrc, destWidth);
1624 SmallVector<SubfieldOp> accesses;
1625 for (
auto *op : structValue.getUsers()) {
1626 assert(isa<SubfieldOp>(op));
1627 auto fieldAccess = cast<SubfieldOp>(op);
1629 fieldAccess.getInput().getType().base().getElementIndex(field);
1630 if (elemIndex && *elemIndex == fieldAccess.getFieldIndex())
1631 accesses.push_back(fieldAccess);
1639LogicalResult FIRRTLModuleLowering::lowerModulePortsAndMoveBody(
1642 ImplicitLocOpBuilder bodyBuilder(oldModule.getLoc(), newModule.getBody());
1649 bodyBuilder.setInsertionPoint(cursor);
1652 SmallVector<PortInfo> firrtlPorts = oldModule.getPorts();
1653 assert(oldModule.getBody().getNumArguments() == firrtlPorts.size() &&
1654 "port count mismatch");
1656 SmallVector<Value, 4> outputs;
1659 auto *outputOp = newModule.getBodyBlock()->getTerminator();
1660 ImplicitLocOpBuilder outputBuilder(oldModule.getLoc(), outputOp);
1662 unsigned nextHWInputArg = 0;
1663 int hwPortIndex = -1;
1664 for (
auto [firrtlPortIndex, port] :
llvm::enumerate(firrtlPorts)) {
1666 auto oldArg = oldModule.getBody().getArgument(firrtlPortIndex);
1669 type_isa<FIRRTLBaseType>(port.type) &&
1670 type_cast<FIRRTLBaseType>(port.type).getBitWidthOrSentinel() == 0;
1674 if (!port.isOutput() && !isZeroWidth) {
1677 Value newArg = newModule.getBody().getArgument(nextHWInputArg++);
1683 oldArg.replaceAllUsesWith(newArg);
1689 if (isZeroWidth && port.isInput()) {
1691 WireOp::create(bodyBuilder, port.type,
1692 "." + port.getName().str() +
".0width_input")
1694 oldArg.replaceAllUsesWith(newArg);
1702 outputs.push_back(value);
1703 assert(oldArg.use_empty() &&
"should have removed all uses of oldArg");
1709 auto newArg = WireOp::create(bodyBuilder, port.type,
1710 "." + port.getName().str() +
".output");
1713 oldArg.replaceAllUsesWith(newArg.getResult());
1716 auto resultHWType =
loweringState.lowerType(port.type, port.loc);
1717 if (!resultHWType.isInteger(0)) {
1720 outputs.push_back(output);
1723 if (
auto sym = newModule.getPort(hwPortIndex).getSym()) {
1724 newArg.setInnerSymAttr(sym);
1725 newModule.setPortSymbolAttr(hwPortIndex, {});
1731 outputOp->setOperands(outputs);
1734 auto &oldBlockInstList = oldModule.getBodyBlock()->getOperations();
1735 auto &newBlockInstList = newModule.getBodyBlock()->getOperations();
1736 newBlockInstList.splice(Block::iterator(cursor), oldBlockInstList,
1737 oldBlockInstList.begin(), oldBlockInstList.end());
1748FIRRTLModuleLowering::lowerFormalBody(verif::FormalOp newOp,
1750 auto builder = OpBuilder::atBlockEnd(&newOp.getBody().front());
1755 auto oldOp = cast<FormalOp>(
loweringState.getOldModule(newOp));
1756 auto moduleName = oldOp.getModuleNameAttr().getAttr();
1757 auto oldModule = cast<FModuleOp>(
1758 loweringState.getInstanceGraph().lookup(moduleName)->getModule());
1759 auto newModule = cast<hw::HWModuleOp>(
loweringState.getNewModule(oldModule));
1762 SmallVector<Value> symbolicInputs;
1763 for (
auto arg : newModule.getBody().getArguments())
1764 symbolicInputs.push_back(
1765 verif::SymbolicValueOp::create(builder, arg.
getLoc(), arg.getType()));
1768 hw::InstanceOp::create(builder, newOp.getLoc(), newModule,
1769 newModule.getNameAttr(), symbolicInputs);
1776FIRRTLModuleLowering::lowerSimulationBody(verif::SimulationOp newOp,
1778 auto builder = OpBuilder::atBlockEnd(newOp.getBody());
1781 auto oldOp = cast<SimulationOp>(
loweringState.getOldModule(newOp));
1782 auto moduleName = oldOp.getModuleNameAttr().getAttr();
1783 auto oldModule = cast<FModuleLike>(
1784 *
loweringState.getInstanceGraph().lookup(moduleName)->getModule());
1786 cast<hw::HWModuleLike>(
loweringState.getNewModule(oldModule));
1790 SmallVector<Value> inputs(newOp.getBody()->args_begin(),
1791 newOp.getBody()->args_end());
1792 auto instOp = hw::InstanceOp::create(builder, newOp.getLoc(), newModule,
1793 newModule.getNameAttr(), inputs);
1794 verif::YieldOp::create(builder, newOp.getLoc(), instOp.getResults());
1804struct FIRRTLLowering :
public FIRRTLVisitor<FIRRTLLowering, LogicalResult> {
1806 FIRRTLLowering(
hw::HWModuleOp module, CircuitLoweringState &circuitState)
1807 : theModule(module), circuitState(circuitState),
1808 builder(module.
getLoc(), module.getContext()), moduleNamespace(module),
1809 backedgeBuilder(builder, module.
getLoc()) {}
1811 LogicalResult
run();
1814 Value getOrCreateClockConstant(seq::ClockConst clock);
1815 Value getOrCreateIntConstant(
const APInt &value);
1816 Value getOrCreateIntConstant(
unsigned numBits, uint64_t val,
1817 bool isSigned =
false) {
1818 return getOrCreateIntConstant(APInt(numBits, val, isSigned));
1820 Attribute getOrCreateAggregateConstantAttribute(Attribute value, Type type);
1821 Value getOrCreateXConstant(
unsigned numBits);
1822 Value getOrCreateZConstant(Type type);
1823 Value getPossiblyInoutLoweredValue(Value value);
1824 Value getLoweredValue(Value value);
1825 Value getLoweredNonClockValue(Value value);
1826 Value getLoweredAndExtendedValue(Value value, Type destType);
1827 Value getLoweredAndExtOrTruncValue(Value value, Type destType);
1828 LogicalResult setLowering(Value orig, Value result);
1829 LogicalResult setPossiblyFoldedLowering(Value orig, Value result);
1830 template <
typename ResultOpType,
typename... CtorArgTypes>
1831 LogicalResult setLoweringTo(Operation *orig, CtorArgTypes... args);
1832 template <
typename ResultOpType,
typename... CtorArgTypes>
1833 LogicalResult setLoweringToLTL(Operation *orig, CtorArgTypes... args);
1834 Backedge createBackedge(Location loc, Type type);
1835 Backedge createBackedge(Value orig, Type type);
1836 bool updateIfBackedge(Value dest, Value src);
1839 bool requiresInnerSymbol(hw::InnerSymbolOpInterface op) {
1844 if (
auto forceable = dyn_cast<Forceable>(op.getOperation()))
1845 if (forceable.isForceable())
1853 hw::InnerSymAttr lowerInnerSymbol(hw::InnerSymbolOpInterface op) {
1854 auto attr = op.getInnerSymAttr();
1858 if (requiresInnerSymbol(op))
1860 op.getContext(), attr, 0,
1865 void runWithInsertionPointAtEndOfBlock(
const std::function<
void(
void)> &fn,
1869 Value getReadValue(Value v);
1871 Value getNonClockValue(Value v);
1873 void addToAlwaysBlock(sv::EventControl clockEdge, Value clock,
1874 sv::ResetType resetStyle, sv::EventControl resetEdge,
1875 Value reset,
const std::function<
void(
void)> &body = {},
1876 const std::function<void(
void)> &resetBody = {});
1877 void addToAlwaysBlock(Value clock,
1878 const std::function<
void(
void)> &body = {}) {
1879 addToAlwaysBlock(sv::EventControl::AtPosEdge, clock, sv::ResetType(),
1880 sv::EventControl(), Value(), body,
1881 std::function<
void(
void)>());
1884 LogicalResult emitGuards(Location loc, ArrayRef<Attribute> guards,
1885 std::function<
void(
void)>
emit);
1886 void addToIfDefBlock(StringRef cond, std::function<
void(
void)> thenCtor,
1887 std::function<
void(
void)> elseCtor = {});
1888 void addToInitialBlock(std::function<
void(
void)> body);
1889 void addIfProceduralBlock(Value cond, std::function<
void(
void)> thenCtor,
1890 std::function<
void(
void)> elseCtor = {});
1891 Value getExtOrTruncAggregateValue(Value array,
FIRRTLBaseType sourceType,
1893 bool allowTruncate);
1894 Value createArrayIndexing(Value array, Value index);
1895 Value createValueWithMuxAnnotation(Operation *op,
bool isMux2);
1897 using FIRRTLVisitor<FIRRTLLowering, LogicalResult>::visitExpr;
1898 using FIRRTLVisitor<FIRRTLLowering, LogicalResult>::visitDecl;
1899 using FIRRTLVisitor<FIRRTLLowering, LogicalResult>::visitStmt;
1902 enum UnloweredOpResult { AlreadyLowered, NowLowered, LoweringFailure };
1903 UnloweredOpResult handleUnloweredOp(Operation *op);
1904 LogicalResult visitExpr(ConstantOp op);
1905 LogicalResult visitExpr(SpecialConstantOp op);
1906 LogicalResult visitExpr(SubindexOp op);
1907 LogicalResult visitExpr(SubaccessOp op);
1908 LogicalResult visitExpr(SubfieldOp op);
1909 LogicalResult visitExpr(VectorCreateOp op);
1910 LogicalResult visitExpr(BundleCreateOp op);
1911 LogicalResult visitExpr(FEnumCreateOp op);
1912 LogicalResult visitExpr(AggregateConstantOp op);
1913 LogicalResult visitExpr(IsTagOp op);
1914 LogicalResult visitExpr(SubtagOp op);
1915 LogicalResult visitExpr(TagExtractOp op);
1918 if (
auto castOp = dyn_cast<mlir::UnrealizedConversionCastOp>(op))
1919 return visitUnrealizedConversionCast(castOp);
1924 LogicalResult visitDecl(WireOp op);
1925 LogicalResult visitDecl(NodeOp op);
1926 LogicalResult visitDecl(RegOp op);
1927 LogicalResult visitDecl(RegResetOp op);
1928 LogicalResult visitDecl(MemOp op);
1929 LogicalResult visitDecl(InstanceOp oldInstance);
1930 LogicalResult visitDecl(VerbatimWireOp op);
1931 LogicalResult visitDecl(ContractOp op);
1934 LogicalResult lowerNoopCast(Operation *op);
1935 LogicalResult visitExpr(AsSIntPrimOp op);
1936 LogicalResult visitExpr(AsUIntPrimOp op);
1937 LogicalResult visitExpr(AsClockPrimOp op);
1938 LogicalResult visitExpr(AsAsyncResetPrimOp op) {
return lowerNoopCast(op); }
1940 LogicalResult visitExpr(HWStructCastOp op);
1941 LogicalResult visitExpr(BitCastOp op);
1943 visitUnrealizedConversionCast(mlir::UnrealizedConversionCastOp op);
1944 LogicalResult visitExpr(CvtPrimOp op);
1945 LogicalResult visitExpr(NotPrimOp op);
1946 LogicalResult visitExpr(NegPrimOp op);
1947 LogicalResult visitExpr(PadPrimOp op);
1948 LogicalResult visitExpr(XorRPrimOp op);
1949 LogicalResult visitExpr(AndRPrimOp op);
1950 LogicalResult visitExpr(OrRPrimOp op);
1953 template <
typename ResultUnsignedOpType,
1954 typename ResultSignedOpType = ResultUnsignedOpType>
1955 LogicalResult lowerBinOp(Operation *op);
1956 template <
typename ResultOpType>
1957 LogicalResult lowerBinOpToVariadic(Operation *op);
1959 template <
typename ResultOpType>
1960 LogicalResult lowerElementwiseLogicalOp(Operation *op);
1962 LogicalResult lowerCmpOp(Operation *op, ICmpPredicate signedOp,
1963 ICmpPredicate unsignedOp);
1964 template <
typename SignedOp,
typename Un
signedOp>
1965 LogicalResult lowerDivLikeOp(Operation *op);
1967 LogicalResult visitExpr(CatPrimOp op);
1969 LogicalResult visitExpr(AndPrimOp op) {
1970 return lowerBinOpToVariadic<comb::AndOp>(op);
1972 LogicalResult visitExpr(OrPrimOp op) {
1973 return lowerBinOpToVariadic<comb::OrOp>(op);
1975 LogicalResult visitExpr(XorPrimOp op) {
1976 return lowerBinOpToVariadic<comb::XorOp>(op);
1978 LogicalResult visitExpr(ElementwiseOrPrimOp op) {
1979 return lowerElementwiseLogicalOp<comb::OrOp>(op);
1981 LogicalResult visitExpr(ElementwiseAndPrimOp op) {
1982 return lowerElementwiseLogicalOp<comb::AndOp>(op);
1984 LogicalResult visitExpr(ElementwiseXorPrimOp op) {
1985 return lowerElementwiseLogicalOp<comb::XorOp>(op);
1987 LogicalResult visitExpr(AddPrimOp op) {
1988 return lowerBinOpToVariadic<comb::AddOp>(op);
1990 LogicalResult visitExpr(EQPrimOp op) {
1991 return lowerCmpOp(op, ICmpPredicate::eq, ICmpPredicate::eq);
1993 LogicalResult visitExpr(NEQPrimOp op) {
1994 return lowerCmpOp(op, ICmpPredicate::ne, ICmpPredicate::ne);
1996 LogicalResult visitExpr(LTPrimOp op) {
1997 return lowerCmpOp(op, ICmpPredicate::slt, ICmpPredicate::ult);
1999 LogicalResult visitExpr(LEQPrimOp op) {
2000 return lowerCmpOp(op, ICmpPredicate::sle, ICmpPredicate::ule);
2002 LogicalResult visitExpr(GTPrimOp op) {
2003 return lowerCmpOp(op, ICmpPredicate::sgt, ICmpPredicate::ugt);
2005 LogicalResult visitExpr(GEQPrimOp op) {
2006 return lowerCmpOp(op, ICmpPredicate::sge, ICmpPredicate::uge);
2009 LogicalResult visitExpr(SubPrimOp op) {
return lowerBinOp<comb::SubOp>(op); }
2010 LogicalResult visitExpr(MulPrimOp op) {
2011 return lowerBinOpToVariadic<comb::MulOp>(op);
2013 LogicalResult visitExpr(DivPrimOp op) {
2014 return lowerDivLikeOp<comb::DivSOp, comb::DivUOp>(op);
2016 LogicalResult visitExpr(RemPrimOp op) {
2017 return lowerDivLikeOp<comb::ModSOp, comb::ModUOp>(op);
2021 LogicalResult visitExpr(IsXIntrinsicOp op);
2022 LogicalResult visitExpr(PlusArgsTestIntrinsicOp op);
2023 LogicalResult visitExpr(PlusArgsValueIntrinsicOp op);
2024 LogicalResult visitStmt(FPGAProbeIntrinsicOp op);
2025 LogicalResult visitExpr(ClockInverterIntrinsicOp op);
2026 LogicalResult visitExpr(ClockDividerIntrinsicOp op);
2027 LogicalResult visitExpr(SizeOfIntrinsicOp op);
2028 LogicalResult visitExpr(ClockGateIntrinsicOp op);
2029 LogicalResult visitExpr(LTLAndIntrinsicOp op);
2030 LogicalResult visitExpr(LTLOrIntrinsicOp op);
2031 LogicalResult visitExpr(LTLIntersectIntrinsicOp op);
2032 LogicalResult visitExpr(LTLDelayIntrinsicOp op);
2033 LogicalResult visitExpr(LTLConcatIntrinsicOp op);
2034 LogicalResult visitExpr(LTLRepeatIntrinsicOp op);
2035 LogicalResult visitExpr(LTLGoToRepeatIntrinsicOp op);
2036 LogicalResult visitExpr(LTLNonConsecutiveRepeatIntrinsicOp op);
2037 LogicalResult visitExpr(LTLNotIntrinsicOp op);
2038 LogicalResult visitExpr(LTLImplicationIntrinsicOp op);
2039 LogicalResult visitExpr(LTLUntilIntrinsicOp op);
2040 LogicalResult visitExpr(LTLEventuallyIntrinsicOp op);
2041 LogicalResult visitExpr(LTLClockIntrinsicOp op);
2043 template <
typename TargetOp,
typename IntrinsicOp>
2044 LogicalResult lowerVerifIntrinsicOp(IntrinsicOp op);
2045 LogicalResult visitStmt(VerifAssertIntrinsicOp op);
2046 LogicalResult visitStmt(VerifAssumeIntrinsicOp op);
2047 LogicalResult visitStmt(VerifCoverIntrinsicOp op);
2048 LogicalResult visitStmt(VerifRequireIntrinsicOp op);
2049 LogicalResult visitStmt(VerifEnsureIntrinsicOp op);
2050 LogicalResult visitExpr(HasBeenResetIntrinsicOp op);
2051 LogicalResult visitStmt(UnclockedAssumeIntrinsicOp op);
2054 LogicalResult visitExpr(BitsPrimOp op);
2055 LogicalResult visitExpr(InvalidValueOp op);
2056 LogicalResult visitExpr(HeadPrimOp op);
2057 LogicalResult visitExpr(ShlPrimOp op);
2058 LogicalResult visitExpr(ShrPrimOp op);
2059 LogicalResult visitExpr(DShlPrimOp op) {
2060 return lowerDivLikeOp<comb::ShlOp, comb::ShlOp>(op);
2062 LogicalResult visitExpr(DShrPrimOp op) {
2063 return lowerDivLikeOp<comb::ShrSOp, comb::ShrUOp>(op);
2065 LogicalResult visitExpr(DShlwPrimOp op) {
2066 return lowerDivLikeOp<comb::ShlOp, comb::ShlOp>(op);
2068 LogicalResult visitExpr(TailPrimOp op);
2069 LogicalResult visitExpr(MuxPrimOp op);
2070 LogicalResult visitExpr(Mux2CellIntrinsicOp op);
2071 LogicalResult visitExpr(Mux4CellIntrinsicOp op);
2072 LogicalResult visitExpr(MultibitMuxOp op);
2073 LogicalResult visitExpr(VerbatimExprOp op);
2074 LogicalResult visitExpr(XMRRefOp op);
2075 LogicalResult visitExpr(XMRDerefOp op);
2078 LogicalResult visitExpr(TimeOp op);
2079 LogicalResult visitExpr(HierarchicalModuleNameOp op);
2082 LogicalResult lowerVerificationStatement(
2083 Operation *op, StringRef labelPrefix, Value clock, Value predicate,
2084 Value enable, StringAttr messageAttr, ValueRange operands,
2085 StringAttr nameAttr,
bool isConcurrent, EventControl eventControl);
2087 LogicalResult visitStmt(SkipOp op);
2089 FailureOr<bool> lowerConnect(Value dest, Value srcVal);
2090 LogicalResult visitStmt(ConnectOp op);
2091 LogicalResult visitStmt(MatchingConnectOp op);
2092 LogicalResult visitStmt(ForceOp op);
2094 std::optional<Value> getLoweredFmtOperand(Value operand);
2095 LogicalResult loweredFmtOperands(ValueRange operands,
2096 SmallVectorImpl<Value> &loweredOperands);
2097 FailureOr<Value> callFileDescriptorLib(
const FileDescriptorInfo &info);
2101 LogicalResult lowerStatementWithFd(
2102 const FileDescriptorInfo &fileDescriptorInfo, Value clock, Value cond,
2103 const std::function<LogicalResult(Value)> &fn,
bool usePrintfCond);
2107 LogicalResult visitPrintfLike(T op,
2108 const FileDescriptorInfo &fileDescriptorInfo,
2109 bool usePrintfCond);
2110 LogicalResult visitStmt(PrintFOp op) {
return visitPrintfLike(op, {},
true); }
2111 LogicalResult visitStmt(FPrintFOp op);
2112 LogicalResult visitStmt(FFlushOp op);
2113 LogicalResult visitStmt(StopOp op);
2114 LogicalResult visitStmt(AssertOp op);
2115 LogicalResult visitStmt(AssumeOp op);
2116 LogicalResult visitStmt(CoverOp op);
2117 LogicalResult visitStmt(AttachOp op);
2118 LogicalResult visitStmt(RefForceOp op);
2119 LogicalResult visitStmt(RefForceInitialOp op);
2120 LogicalResult visitStmt(RefReleaseOp op);
2121 LogicalResult visitStmt(RefReleaseInitialOp op);
2122 LogicalResult visitStmt(BindOp op);
2124 FailureOr<Value> lowerSubindex(SubindexOp op, Value input);
2125 FailureOr<Value> lowerSubaccess(SubaccessOp op, Value input);
2126 FailureOr<Value> lowerSubfield(SubfieldOp op, Value input);
2128 LogicalResult fixupLTLOps();
2131 return circuitState.lowerType(type, builder.getLoc());
2139 CircuitLoweringState &circuitState;
2142 ImplicitLocOpBuilder builder;
2147 DenseMap<Value, Value> valueMapping;
2151 DenseMap<Value, Value> fromClockMapping;
2155 DenseMap<Attribute, Value> hwConstantMap;
2156 DenseMap<std::pair<Attribute, Type>, Attribute> hwAggregateConstantMap;
2160 DenseMap<unsigned, Value> hwConstantXMap;
2161 DenseMap<Type, Value> hwConstantZMap;
2167 DenseMap<Value, Value> readInOutCreated;
2170 DenseMap<StringAttr, sv::RegOp> fileNameToFileDescriptor;
2174 using AlwaysKeyType = std::tuple<
Block *, sv::EventControl, Value,
2175 sv::ResetType, sv::EventControl, Value>;
2192 llvm::MapVector<Value, Value> backedges;
2199 DenseSet<Operation *> maybeUnusedValues;
2201 void maybeUnused(Operation *op) { maybeUnusedValues.insert(op); }
2202 void maybeUnused(Value value) {
2203 if (
auto *op = value.getDefiningOp())
2215 SetVector<Operation *> ltlOpFixupWorklist;
2220 SmallVector<std::pair<Block::iterator, Block::iterator>> worklist;
2222 void addToWorklist(Block &block) {
2223 worklist.push_back({block.begin(), block.end()});
2225 void addToWorklist(Region ®ion) {
2226 for (
auto &block :
llvm::reverse(region))
2227 addToWorklist(block);
2238LogicalResult FIRRTLModuleLowering::lowerFileBody(emit::FileOp fileOp) {
2239 OpBuilder b(&getContext());
2240 fileOp->walk([&](Operation *op) {
2241 if (
auto bindOp = dyn_cast<BindOp>(op)) {
2242 b.setInsertionPointAfter(bindOp);
2243 sv::BindOp::create(b, bindOp.getLoc(), bindOp.getInstanceAttr());
2251FIRRTLModuleLowering::lowerBody(Operation *op,
2253 if (
auto moduleOp = dyn_cast<hw::HWModuleOp>(op))
2255 if (
auto formalOp = dyn_cast<verif::FormalOp>(op))
2257 if (
auto simulationOp = dyn_cast<verif::SimulationOp>(op))
2259 if (
auto fileOp = dyn_cast<emit::FileOp>(op))
2260 return lowerFileBody(fileOp);
2265LogicalResult FIRRTLLowering::run() {
2268 for (
auto arg : theModule.
getBodyBlock()->getArguments())
2269 if (failed(setLowering(arg, arg)))
2276 addToWorklist(theModule.getBody());
2277 SmallVector<Operation *, 16> opsToRemove;
2279 while (!worklist.empty()) {
2280 auto &[opsIt, opsEnd] = worklist.back();
2281 if (opsIt == opsEnd) {
2282 worklist.pop_back();
2285 Operation *op = &*opsIt++;
2287 builder.setInsertionPoint(op);
2288 builder.setLoc(op->getLoc());
2289 auto done = succeeded(dispatchVisitor(op));
2290 circuitState.processRemainingAnnotations(op,
AnnotationSet(op));
2292 opsToRemove.push_back(op);
2294 switch (handleUnloweredOp(op)) {
2295 case AlreadyLowered:
2298 opsToRemove.push_back(op);
2300 case LoweringFailure:
2312 for (
auto &[backedge, value] : backedges) {
2313 SmallVector<Location> driverLocs;
2319 if (backedge == value) {
2320 Location edgeLoc = backedge.getLoc();
2321 if (driverLocs.empty()) {
2322 mlir::emitError(edgeLoc,
"sink does not have a driver");
2324 auto diag = mlir::emitError(edgeLoc,
"sink in combinational loop");
2325 for (
auto loc : driverLocs)
2326 diag.attachNote(loc) <<
"through driver here";
2332 auto *it = backedges.find(value);
2333 if (it == backedges.end())
2336 driverLocs.push_back(value.getLoc());
2339 if (
auto *defOp = backedge.getDefiningOp())
2340 maybeUnusedValues.erase(defOp);
2341 backedge.replaceAllUsesWith(value);
2349 while (!opsToRemove.empty()) {
2350 auto *op = opsToRemove.pop_back_val();
2355 for (
auto result : op->getResults()) {
2359 auto builder = OpBuilder::atBlockBegin(theModule.getBodyBlock());
2361 builder.getIntegerType(0), 0);
2362 maybeUnusedValues.insert(zeroI0);
2364 result.replaceAllUsesWith(zeroI0);
2367 if (!op->use_empty()) {
2368 auto d = op->emitOpError(
2369 "still has uses; should remove ops in reverse order of visitation");
2370 SmallPtrSet<Operation *, 2> visited;
2371 for (
auto *user : op->getUsers())
2372 if (visited.insert(user).second)
2373 d.attachNote(user->
getLoc())
2374 <<
"used by " << user->
getName() <<
" op";
2377 maybeUnusedValues.erase(op);
2382 while (!maybeUnusedValues.empty()) {
2383 auto it = maybeUnusedValues.begin();
2385 maybeUnusedValues.erase(it);
2386 if (!isOpTriviallyDead(op))
2388 for (
auto operand : op->getOperands())
2389 if (auto *defOp = operand.getDefiningOp())
2390 maybeUnusedValues.insert(defOp);
2396 if (failed(fixupLTLOps()))
2407Value FIRRTLLowering::getOrCreateClockConstant(seq::ClockConst clock) {
2408 auto attr = seq::ClockConstAttr::get(theModule.getContext(), clock);
2410 auto &entry = hwConstantMap[attr];
2414 OpBuilder entryBuilder(&theModule.getBodyBlock()->front());
2415 entry = seq::ConstClockOp::create(entryBuilder, builder.getLoc(), attr);
2421Value FIRRTLLowering::getOrCreateIntConstant(
const APInt &value) {
2422 auto attr = builder.getIntegerAttr(
2423 builder.getIntegerType(value.getBitWidth()), value);
2425 auto &entry = hwConstantMap[attr];
2429 OpBuilder entryBuilder(&theModule.getBodyBlock()->front());
2436Attribute FIRRTLLowering::getOrCreateAggregateConstantAttribute(Attribute value,
2439 if (hw::type_isa<IntegerType>(type))
2440 return builder.getIntegerAttr(type, cast<IntegerAttr>(value).getValue());
2442 auto cache = hwAggregateConstantMap.lookup({value, type});
2447 SmallVector<Attribute> values;
2448 for (
auto e :
llvm::enumerate(cast<ArrayAttr>(value))) {
2450 if (
auto array = hw::type_dyn_cast<hw::ArrayType>(type))
2451 subType = array.getElementType();
2452 else if (
auto structType = hw::type_dyn_cast<hw::StructType>(type))
2453 subType = structType.getElements()[e.index()].type;
2455 assert(
false &&
"type must be either array or struct");
2457 values.push_back(getOrCreateAggregateConstantAttribute(e.value(), subType));
2461 if (hw::type_isa<hw::ArrayType>(type))
2462 std::reverse(values.begin(), values.end());
2464 auto &entry = hwAggregateConstantMap[{value, type}];
2465 entry = builder.getArrayAttr(values);
2473 const std::function<LogicalResult()> &fn) {
2474 assert(failedOperand &&
"Should be called on the failed operand");
2482Value FIRRTLLowering::getOrCreateXConstant(
unsigned numBits) {
2484 auto &entry = hwConstantXMap[numBits];
2488 OpBuilder entryBuilder(&theModule.getBodyBlock()->front());
2489 entry = sv::ConstantXOp::create(entryBuilder, builder.getLoc(),
2490 entryBuilder.getIntegerType(numBits));
2494Value FIRRTLLowering::getOrCreateZConstant(Type type) {
2495 auto &entry = hwConstantZMap[type];
2497 OpBuilder entryBuilder(&theModule.getBodyBlock()->front());
2498 entry = sv::ConstantZOp::create(entryBuilder, builder.getLoc(), type);
2507Value FIRRTLLowering::getPossiblyInoutLoweredValue(Value value) {
2509 if (
auto lowering = valueMapping.lookup(value)) {
2510 assert(!isa<FIRRTLType>(lowering.getType()) &&
2511 "Lowered value should be a non-FIRRTL value");
2520Value FIRRTLLowering::getLoweredValue(Value value) {
2521 auto result = getPossiblyInoutLoweredValue(value);
2527 if (isa<hw::InOutType>(result.getType()))
2528 return getReadValue(result);
2534Value FIRRTLLowering::getLoweredNonClockValue(Value value) {
2535 auto result = getLoweredValue(value);
2539 if (hw::type_isa<seq::ClockType>(result.getType()))
2540 return getNonClockValue(result);
2548Value FIRRTLLowering::getExtOrTruncAggregateValue(Value array,
2551 bool allowTruncate) {
2552 SmallVector<Value> resultBuffer;
2557 auto srcWidth = firrtl::type_cast<IntType>(sourceType).getWidthOrSentinel();
2558 auto destWidth = firrtl::type_cast<IntType>(destType).getWidthOrSentinel();
2559 auto resultType = builder.getIntegerType(destWidth);
2561 if (srcWidth == destWidth)
2564 if (srcWidth > destWidth) {
2568 builder.emitError(
"operand should not be a truncation");
2572 if (firrtl::type_cast<IntType>(sourceType).isSigned())
2573 return comb::createOrFoldSExt(value, resultType, builder);
2574 auto zero = getOrCreateIntConstant(destWidth - srcWidth, 0);
2582 return TypeSwitch<FIRRTLBaseType, LogicalResult>(srcType)
2583 .Case<FVectorType>([&](
auto srcVectorType) {
2584 auto destVectorType = firrtl::type_cast<FVectorType>(destType);
2585 unsigned size = resultBuffer.size();
2586 unsigned indexWidth =
2588 for (
size_t i = 0, e = std::min(srcVectorType.getNumElements(),
2589 destVectorType.getNumElements());
2591 auto iIdx = getOrCreateIntConstant(indexWidth, i);
2593 if (failed(recurse(arrayIndex, srcVectorType.getElementType(),
2594 destVectorType.getElementType())))
2597 SmallVector<Value> temp(resultBuffer.begin() + size,
2598 resultBuffer.end());
2600 resultBuffer.resize(size);
2601 resultBuffer.push_back(array);
2604 .Case<BundleType>([&](BundleType srcStructType) {
2605 auto destStructType = firrtl::type_cast<BundleType>(destType);
2606 unsigned size = resultBuffer.size();
2609 if (destStructType.getNumElements() != srcStructType.getNumElements())
2612 for (
auto elem :
llvm::enumerate(destStructType)) {
2613 auto structExtract =
2615 if (failed(recurse(structExtract,
2616 srcStructType.getElementType(elem.index()),
2617 destStructType.getElementType(elem.index()))))
2620 SmallVector<Value> temp(resultBuffer.begin() + size,
2621 resultBuffer.end());
2624 resultBuffer.resize(size);
2625 resultBuffer.push_back(newStruct);
2628 .Case<IntType>([&](
auto) {
2629 if (
auto result = cast(src, srcType, destType)) {
2630 resultBuffer.push_back(result);
2635 .Default([&](
auto) {
return failure(); });
2638 if (failed(recurse(array, sourceType, destType)))
2641 assert(resultBuffer.size() == 1 &&
2642 "resultBuffer must only contain a result array if `success` is true");
2643 return resultBuffer[0];
2651Value FIRRTLLowering::getLoweredAndExtendedValue(Value src, Type target) {
2652 auto srcType = cast<FIRRTLBaseType>(src.getType());
2653 auto dstType = cast<FIRRTLBaseType>(target);
2654 auto loweredSrc = getLoweredValue(src);
2657 auto dstWidth = dstType.getBitWidthOrSentinel();
2673 return getOrCreateIntConstant(dstWidth, 0);
2676 auto loweredSrcType = loweredSrc.getType();
2677 auto loweredDstType =
lowerType(dstType);
2680 if (loweredSrcType == loweredDstType)
2684 if (dstWidth == srcType.getBitWidthOrSentinel()) {
2686 if (loweredSrcType != loweredDstType &&
2687 (isa<hw::TypeAliasType>(loweredSrcType) ||
2688 isa<hw::TypeAliasType>(loweredDstType))) {
2689 return builder.createOrFold<
hw::BitcastOp>(loweredDstType, loweredSrc);
2694 if (isa<hw::ArrayType, hw::StructType>(loweredSrcType))
2695 return getExtOrTruncAggregateValue(loweredSrc, srcType, dstType,
2698 if (isa<seq::ClockType>(loweredSrcType)) {
2699 builder.emitError(
"cannot use clock type as an integer");
2703 auto intSourceType = dyn_cast<IntegerType>(loweredSrcType);
2704 if (!intSourceType) {
2705 builder.emitError(
"operand of type ")
2706 << loweredSrcType <<
" cannot be used as an integer";
2710 auto loweredSrcWidth = intSourceType.getWidth();
2711 if (loweredSrcWidth ==
unsigned(dstWidth))
2714 if (loweredSrcWidth >
unsigned(dstWidth)) {
2715 builder.emitError(
"operand should not be a truncation");
2720 auto valueFIRType = type_cast<FIRRTLBaseType>(src.getType()).getPassiveType();
2721 if (type_cast<IntType>(valueFIRType).isSigned())
2722 return comb::createOrFoldSExt(loweredSrc, loweredDstType, builder);
2724 auto zero = getOrCreateIntConstant(dstWidth - loweredSrcWidth, 0);
2733Value FIRRTLLowering::getLoweredAndExtOrTruncValue(Value value, Type destType) {
2734 assert(type_isa<FIRRTLBaseType>(value.getType()) &&
2735 type_isa<FIRRTLBaseType>(destType) &&
2736 "input/output value should be FIRRTL");
2739 auto destWidth = type_cast<FIRRTLBaseType>(destType).getBitWidthOrSentinel();
2740 if (destWidth == -1)
2743 auto result = getLoweredValue(value);
2755 return getOrCreateIntConstant(destWidth, 0);
2759 if (isa<hw::ArrayType, hw::StructType>(result.getType())) {
2761 if (destType == value.getType())
2764 return getExtOrTruncAggregateValue(
2765 result, type_cast<FIRRTLBaseType>(value.getType()),
2766 type_cast<FIRRTLBaseType>(destType),
2770 auto srcWidth = type_cast<IntegerType>(result.getType()).getWidth();
2771 if (srcWidth ==
unsigned(destWidth))
2777 if (srcWidth >
unsigned(destWidth)) {
2778 auto resultType = builder.getIntegerType(destWidth);
2782 auto resultType = builder.getIntegerType(destWidth);
2786 type_cast<FIRRTLBaseType>(value.getType()).getPassiveType();
2787 if (type_cast<IntType>(valueFIRType).isSigned())
2788 return comb::createOrFoldSExt(result, resultType, builder);
2790 auto zero = getOrCreateIntConstant(destWidth - srcWidth, 0);
2804std::optional<Value> FIRRTLLowering::getLoweredFmtOperand(Value operand) {
2806 if (type_isa<FStringType>(operand.getType())) {
2807 if (isa<TimeOp>(operand.getDefiningOp()))
2808 return sv::TimeOp::create(builder);
2809 if (isa<HierarchicalModuleNameOp>(operand.getDefiningOp()))
2813 auto loweredValue = getLoweredValue(operand);
2814 if (!loweredValue) {
2818 loweredValue = getOrCreateIntConstant(1, 0);
2823 if (
auto intTy = firrtl::type_cast<IntType>(operand.getType()))
2824 if (intTy.isSigned())
2825 loweredValue = sv::SystemFunctionOp::create(
2826 builder, loweredValue.getType(),
"signed", loweredValue);
2828 return loweredValue;
2832FIRRTLLowering::loweredFmtOperands(mlir::ValueRange operands,
2833 SmallVectorImpl<Value> &loweredOperands) {
2834 for (
auto operand : operands) {
2835 std::optional<Value> loweredValue = getLoweredFmtOperand(operand);
2840 loweredOperands.push_back(*loweredValue);
2845LogicalResult FIRRTLLowering::lowerStatementWithFd(
2846 const FileDescriptorInfo &fileDescriptor, Value clock, Value cond,
2847 const std::function<LogicalResult(Value)> &fn,
bool usePrintfCond) {
2849 bool failed =
false;
2850 circuitState.addMacroDecl(builder.getStringAttr(
"SYNTHESIS"));
2851 addToIfDefBlock(
"SYNTHESIS", std::function<
void()>(), [&]() {
2852 addToAlwaysBlock(clock, [&]() {
2855 circuitState.usedPrintf =
true;
2857 circuitState.addFragment(theModule,
"PRINTF_COND_FRAGMENT");
2860 Value ifCond = cond;
2861 if (usePrintfCond) {
2863 sv::MacroRefExprOp::create(builder, cond.getType(),
"PRINTF_COND_");
2864 ifCond = builder.createOrFold<
comb::AndOp>(ifCond, cond,
true);
2867 addIfProceduralBlock(ifCond, [&]() {
2871 if (fileDescriptor.isDefaultFd()) {
2873 fd = hw::ConstantOp::create(builder, APInt(32, 0x80000002));
2876 auto fdOrError = callFileDescriptorLib(fileDescriptor);
2877 if (llvm::failed(fdOrError)) {
2883 failed = llvm::failed(fn(fd));
2887 return failure(failed);
2891FIRRTLLowering::callFileDescriptorLib(
const FileDescriptorInfo &info) {
2892 circuitState.usedFileDescriptorLib =
true;
2893 circuitState.addFragment(theModule,
"CIRCT_LIB_LOGGING_FRAGMENT");
2896 if (
info.isSubstitutionRequired()) {
2897 SmallVector<Value> fileNameOperands;
2898 if (failed(loweredFmtOperands(
info.getSubstitutions(), fileNameOperands)))
2901 fileName = sv::SFormatFOp::create(builder,
info.getOutputFileFormat(),
2906 fileName = sv::ConstantStrOp::create(builder,
info.getOutputFileFormat())
2910 return sv::FuncCallProceduralOp::create(
2911 builder, mlir::TypeRange{builder.getIntegerType(32)},
2912 builder.getStringAttr(
"__circt_lib_logging::FileDescriptor::get"),
2913 ValueRange{fileName})
2923LogicalResult FIRRTLLowering::setLowering(Value orig, Value result) {
2924 if (
auto origType = dyn_cast<FIRRTLType>(orig.getType())) {
2925 assert((!result || !type_isa<FIRRTLType>(result.getType())) &&
2926 "Lowering didn't turn a FIRRTL value into a non-FIRRTL value");
2930 auto srcWidth = baseType.getPassiveType().getBitWidthOrSentinel();
2933 if (srcWidth != -1) {
2935 assert((srcWidth != 0) &&
2936 "Lowering produced value for zero width source");
2938 assert((srcWidth == 0) &&
2939 "Lowering produced null value but source wasn't zero width");
2943 assert(result &&
"Lowering of foreign type produced null value");
2946 auto &slot = valueMapping[orig];
2947 assert(!slot &&
"value lowered multiple times");
2954LogicalResult FIRRTLLowering::setPossiblyFoldedLowering(Value orig,
2958 if (
auto cst = dyn_cast_or_null<hw::ConstantOp>(result.getDefiningOp())) {
2959 auto &entry = hwConstantMap[cst.getValueAttr()];
2970 cst->moveBefore(&theModule.getBodyBlock()->front());
2974 return setLowering(orig, result);
2979template <
typename ResultOpType,
typename... CtorArgTypes>
2980LogicalResult FIRRTLLowering::setLoweringTo(Operation *orig,
2981 CtorArgTypes... args) {
2982 auto result = builder.createOrFold<ResultOpType>(args...);
2983 if (
auto *op = result.getDefiningOp())
2985 return setPossiblyFoldedLowering(orig->getResult(0), result);
2992template <
typename ResultOpType,
typename... CtorArgTypes>
2993LogicalResult FIRRTLLowering::setLoweringToLTL(Operation *orig,
2994 CtorArgTypes... args) {
2995 auto result = builder.createOrFold<ResultOpType>(args...);
2996 if (
auto *op = result.getDefiningOp())
2997 ltlOpFixupWorklist.insert(op);
2998 return setPossiblyFoldedLowering(orig->getResult(0), result);
3007Backedge FIRRTLLowering::createBackedge(Location loc, Type type) {
3008 auto backedge = backedgeBuilder.
get(type, loc);
3009 backedges.insert({backedge, backedge});
3017Backedge FIRRTLLowering::createBackedge(Value orig, Type type) {
3018 auto backedge = createBackedge(orig.getLoc(), type);
3019 (void)setLowering(orig, backedge);
3025bool FIRRTLLowering::updateIfBackedge(Value dest, Value src) {
3026 auto backedgeIt = backedges.find(dest);
3027 if (backedgeIt == backedges.end())
3029 backedgeIt->second = src;
3037void FIRRTLLowering::runWithInsertionPointAtEndOfBlock(
3038 const std::function<
void(
void)> &fn, Region ®ion) {
3042 auto oldIP = builder.saveInsertionPoint();
3044 builder.setInsertionPointToEnd(®ion.front());
3046 builder.restoreInsertionPoint(oldIP);
3050Value FIRRTLLowering::getReadValue(Value v) {
3051 Value result = readInOutCreated.lookup(v);
3057 auto oldIP = builder.saveInsertionPoint();
3058 if (
auto *vOp = v.getDefiningOp()) {
3059 builder.setInsertionPointAfter(vOp);
3063 builder.setInsertionPoint(&theModule.getBodyBlock()->front());
3068 if (
auto arrayIndexInout = v.getDefiningOp<sv::ArrayIndexInOutOp>()) {
3069 result = getReadValue(arrayIndexInout.getInput());
3071 arrayIndexInout.getIndex());
3076 builder.restoreInsertionPoint(oldIP);
3077 readInOutCreated.insert({v, result});
3081Value FIRRTLLowering::getNonClockValue(Value v) {
3082 auto it = fromClockMapping.try_emplace(v, Value{});
3084 ImplicitLocOpBuilder builder(v.getLoc(), v.getContext());
3085 builder.setInsertionPointAfterValue(v);
3086 it.first->second = seq::FromClockOp::create(builder, v);
3088 return it.first->second;
3091void FIRRTLLowering::addToAlwaysBlock(
3092 sv::EventControl clockEdge, Value clock, sv::ResetType resetStyle,
3093 sv::EventControl resetEdge, Value reset,
3094 const std::function<
void(
void)> &body,
3095 const std::function<
void(
void)> &resetBody) {
3096 AlwaysKeyType key{builder.getBlock(), clockEdge, clock,
3097 resetStyle, resetEdge, reset};
3098 sv::AlwaysOp alwaysOp;
3099 sv::IfOp insideIfOp;
3100 std::tie(alwaysOp, insideIfOp) = alwaysBlocks.lookup(key);
3104 assert(resetStyle != sv::ResetType::NoReset);
3117 auto createIfOp = [&]() {
3120 insideIfOp = sv::IfOp::create(
3121 builder, reset, [] {}, [] {});
3123 if (resetStyle == sv::ResetType::AsyncReset) {
3124 sv::EventControl events[] = {clockEdge, resetEdge};
3125 Value clocks[] = {clock, reset};
3127 alwaysOp = sv::AlwaysOp::create(builder, events, clocks, [&]() {
3128 if (resetEdge == sv::EventControl::AtNegEdge)
3129 llvm_unreachable(
"negative edge for reset is not expected");
3133 alwaysOp = sv::AlwaysOp::create(builder, clockEdge, clock, createIfOp);
3137 alwaysOp = sv::AlwaysOp::create(builder, clockEdge, clock);
3138 insideIfOp =
nullptr;
3140 alwaysBlocks[key] = {alwaysOp, insideIfOp};
3144 assert(insideIfOp &&
"reset body must be initialized before");
3145 runWithInsertionPointAtEndOfBlock(resetBody, insideIfOp.getThenRegion());
3146 runWithInsertionPointAtEndOfBlock(body, insideIfOp.getElseRegion());
3148 runWithInsertionPointAtEndOfBlock(body, alwaysOp.getBody());
3154 alwaysOp->moveBefore(builder.getInsertionBlock(),
3155 builder.getInsertionPoint());
3158LogicalResult FIRRTLLowering::emitGuards(Location loc,
3159 ArrayRef<Attribute> guards,
3160 std::function<
void(
void)>
emit) {
3161 if (guards.empty()) {
3165 auto guard = dyn_cast<StringAttr>(guards[0]);
3167 return mlir::emitError(loc,
3168 "elements in `guards` array must be `StringAttr`");
3171 circuitState.addMacroDecl(builder.getStringAttr(guard.getValue()));
3172 LogicalResult result = LogicalResult::failure();
3173 addToIfDefBlock(guard.getValue(), [&]() {
3174 result = emitGuards(loc, guards.drop_front(), emit);
3179void FIRRTLLowering::addToIfDefBlock(StringRef cond,
3180 std::function<
void(
void)> thenCtor,
3181 std::function<
void(
void)> elseCtor) {
3182 auto condAttr = builder.getStringAttr(cond);
3183 auto op = ifdefBlocks.lookup({builder.getBlock(), condAttr});
3185 runWithInsertionPointAtEndOfBlock(thenCtor, op.getThenRegion());
3186 runWithInsertionPointAtEndOfBlock(elseCtor, op.getElseRegion());
3191 op->moveBefore(builder.getInsertionBlock(), builder.getInsertionPoint());
3193 ifdefBlocks[{builder.getBlock(), condAttr}] =
3194 sv::IfDefOp::create(builder, condAttr, thenCtor, elseCtor);
3198void FIRRTLLowering::addToInitialBlock(std::function<
void(
void)> body) {
3199 auto op = initialBlocks.lookup(builder.getBlock());
3201 runWithInsertionPointAtEndOfBlock(body, op.getBody());
3206 op->moveBefore(builder.getInsertionBlock(), builder.getInsertionPoint());
3208 initialBlocks[builder.getBlock()] = sv::InitialOp::create(builder, body);
3212void FIRRTLLowering::addIfProceduralBlock(Value cond,
3213 std::function<
void(
void)> thenCtor,
3214 std::function<
void(
void)> elseCtor) {
3217 auto insertIt = builder.getInsertionPoint();
3218 if (insertIt != builder.getBlock()->begin())
3219 if (
auto ifOp = dyn_cast<sv::IfOp>(*--insertIt)) {
3220 if (ifOp.getCond() == cond) {
3221 runWithInsertionPointAtEndOfBlock(thenCtor, ifOp.getThenRegion());
3222 runWithInsertionPointAtEndOfBlock(elseCtor, ifOp.getElseRegion());
3227 sv::IfOp::create(builder, cond, thenCtor, elseCtor);
3239FIRRTLLowering::UnloweredOpResult
3240FIRRTLLowering::handleUnloweredOp(Operation *op) {
3242 if (!op->getRegions().empty() &&
3243 isa_and_nonnull<FIRRTLDialect>(op->getDialect())) {
3244 op->emitOpError(
"must explicitly handle its regions");
3245 return LoweringFailure;
3252 if (!isa_and_nonnull<FIRRTLDialect>(op->getDialect())) {
3254 for (
auto ®ion : op->getRegions())
3255 addToWorklist(region);
3256 for (
auto &operand : op->getOpOperands())
3257 if (auto lowered = getPossiblyInoutLoweredValue(operand.
get()))
3258 operand.set(lowered);
3259 for (
auto result : op->getResults())
3260 (void)setLowering(result, result);
3261 return AlreadyLowered;
3273 if (op->getNumResults() == 1) {
3274 auto resultType = op->getResult(0).getType();
3275 if (type_isa<FIRRTLBaseType>(resultType) &&
3277 (
isExpression(op) || isa<mlir::UnrealizedConversionCastOp>(op))) {
3279 (void)setLowering(op->getResult(0), Value());
3283 op->emitOpError(
"LowerToHW couldn't handle this operation");
3284 return LoweringFailure;
3287LogicalResult FIRRTLLowering::visitExpr(ConstantOp op) {
3290 return setLowering(op, Value());
3292 return setLowering(op, getOrCreateIntConstant(op.getValue()));
3295LogicalResult FIRRTLLowering::visitExpr(SpecialConstantOp op) {
3297 if (isa<ClockType>(op.getType())) {
3298 cst = getOrCreateClockConstant(op.getValue() ? seq::ClockConst::High
3299 :
seq::ClockConst::Low);
3301 cst = getOrCreateIntConstant(APInt( 1, op.getValue()));
3303 return setLowering(op, cst);
3306FailureOr<Value> FIRRTLLowering::lowerSubindex(SubindexOp op, Value input) {
3307 auto iIdx = getOrCreateIntConstant(
3309 firrtl::type_cast<FVectorType>(op.getInput().getType())
3316 if (isa<sv::InOutType>(input.getType()))
3317 result = builder.createOrFold<sv::ArrayIndexInOutOp>(input, iIdx);
3320 if (
auto *definingOp = result.getDefiningOp())
3325FailureOr<Value> FIRRTLLowering::lowerSubaccess(SubaccessOp op, Value input) {
3326 Value valueIdx = getLoweredAndExtOrTruncValue(
3328 UIntType::get(op->getContext(),
3330 firrtl::type_cast<FVectorType>(op.getInput().getType())
3331 .getNumElements())));
3333 op->emitError() <<
"input lowering failed";
3340 if (isa<sv::InOutType>(input.getType()))
3341 result = builder.createOrFold<sv::ArrayIndexInOutOp>(input, valueIdx);
3343 result = createArrayIndexing(input, valueIdx);
3344 if (
auto *definingOp = result.getDefiningOp())
3349FailureOr<Value> FIRRTLLowering::lowerSubfield(SubfieldOp op, Value input) {
3350 auto resultType =
lowerType(op->getResult(0).getType());
3351 if (!resultType || !input) {
3352 op->emitError() <<
"subfield type lowering failed";
3358 auto field = firrtl::type_cast<BundleType>(op.getInput().getType())
3359 .getElementName(op.getFieldIndex());
3361 if (isa<sv::InOutType>(input.getType()))
3362 result = builder.createOrFold<sv::StructFieldInOutOp>(input, field);
3365 if (
auto *definingOp = result.getDefiningOp())
3370LogicalResult FIRRTLLowering::visitExpr(SubindexOp op) {
3372 return setLowering(op, Value());
3374 auto input = getPossiblyInoutLoweredValue(op.getInput());
3376 return op.emitError() <<
"input lowering failed";
3378 auto result = lowerSubindex(op, input);
3381 return setLowering(op, *result);
3384LogicalResult FIRRTLLowering::visitExpr(SubaccessOp op) {
3386 return setLowering(op, Value());
3388 auto input = getPossiblyInoutLoweredValue(op.getInput());
3390 return op.emitError() <<
"input lowering failed";
3392 auto result = lowerSubaccess(op, input);
3395 return setLowering(op, *result);
3398LogicalResult FIRRTLLowering::visitExpr(SubfieldOp op) {
3401 if (getLoweredValue(op) || !op.getInput())
3405 return setLowering(op, Value());
3407 auto input = getPossiblyInoutLoweredValue(op.getInput());
3409 return op.emitError() <<
"input lowering failed";
3411 auto result = lowerSubfield(op, input);
3414 return setLowering(op, *result);
3417LogicalResult FIRRTLLowering::visitExpr(VectorCreateOp op) {
3418 auto resultType =
lowerType(op.getResult().getType());
3419 SmallVector<Value> operands;
3421 for (
auto oper :
llvm::reverse(op.getOperands())) {
3422 auto val = getLoweredValue(oper);
3425 operands.push_back(val);
3427 return setLoweringTo<hw::ArrayCreateOp>(op, resultType, operands);
3430LogicalResult FIRRTLLowering::visitExpr(BundleCreateOp op) {
3431 auto resultType =
lowerType(op.getResult().getType());
3432 SmallVector<Value> operands;
3433 for (
auto oper : op.getOperands()) {
3434 auto val = getLoweredValue(oper);
3437 operands.push_back(val);
3439 return setLoweringTo<hw::StructCreateOp>(op, resultType, operands);
3442LogicalResult FIRRTLLowering::visitExpr(FEnumCreateOp op) {
3445 return setLowering(op, Value());
3447 auto input = getLoweredValue(op.getInput());
3448 auto tagName = op.getFieldNameAttr();
3449 auto oldType = op.getType().base();
3451 auto element = *oldType.getElement(op.getFieldNameAttr());
3453 if (
auto structType = dyn_cast<hw::StructType>(newType)) {
3454 auto tagType = structType.getFieldType(
"tag");
3455 auto tagValue = IntegerAttr::get(tagType, element.value.getValue());
3456 auto tag = sv::LocalParamOp::create(builder, op.getLoc(), tagType, tagValue,
3458 auto bodyType = structType.getFieldType(
"body");
3459 auto body = hw::UnionCreateOp::create(builder, bodyType, tagName, input);
3460 SmallVector<Value> operands = {tag.getResult(), body.getResult()};
3461 return setLoweringTo<hw::StructCreateOp>(op, structType, operands);
3463 auto tagValue = IntegerAttr::get(newType, element.value.getValue());
3464 return setLoweringTo<sv::LocalParamOp>(op, newType, tagValue, tagName);
3467LogicalResult FIRRTLLowering::visitExpr(AggregateConstantOp op) {
3468 auto resultType =
lowerType(op.getResult().getType());
3470 getOrCreateAggregateConstantAttribute(op.getFieldsAttr(), resultType);
3472 return setLoweringTo<hw::AggregateConstantOp>(op, resultType,
3473 cast<ArrayAttr>(attr));
3476LogicalResult FIRRTLLowering::visitExpr(IsTagOp op) {
3478 auto tagName = op.getFieldNameAttr();
3479 auto lhs = getLoweredValue(op.getInput());
3480 if (isa<hw::StructType>(lhs.getType()))
3483 auto index = op.getFieldIndex();
3484 auto enumType = op.getInput().getType().base();
3485 auto tagValue = enumType.getElementValueAttr(index);
3486 auto tagValueType = IntegerType::get(op.getContext(), enumType.getTagWidth());
3487 auto loweredTagValue = IntegerAttr::get(tagValueType, tagValue.getValue());
3488 auto rhs = sv::LocalParamOp::create(builder, op.getLoc(), tagValueType,
3489 loweredTagValue, tagName);
3491 Type resultType = builder.getIntegerType(1);
3492 return setLoweringTo<comb::ICmpOp>(op, resultType, ICmpPredicate::eq, lhs,
3496LogicalResult FIRRTLLowering::visitExpr(SubtagOp op) {
3499 return setLowering(op, Value());
3501 auto tagName = op.getFieldNameAttr();
3502 auto input = getLoweredValue(op.getInput());
3504 return setLoweringTo<hw::UnionExtractOp>(op, field, tagName);
3507LogicalResult FIRRTLLowering::visitExpr(TagExtractOp op) {
3510 return setLowering(op, Value());
3512 auto input = getLoweredValue(op.getInput());
3518 if (isa<hw::StructType>(input.getType())) {
3519 return setLoweringTo<hw::StructExtractOp>(op, input,
"tag");
3524 return setLowering(op, input);
3531LogicalResult FIRRTLLowering::visitDecl(WireOp op) {
3532 auto origResultType = op.getResult().getType();
3536 if (!type_isa<FIRRTLType>(origResultType)) {
3537 createBackedge(op.getResult(), origResultType);
3541 auto resultType =
lowerType(origResultType);
3545 if (resultType.isInteger(0)) {
3546 if (op.getInnerSym())
3547 return op.emitError(
"zero width wire is referenced by name [")
3548 << *op.getInnerSym() <<
"] (e.g. in an XMR) but must be removed";
3549 return setLowering(op.getResult(), Value());
3553 auto innerSym = lowerInnerSymbol(op);
3554 auto name = op.getNameAttr();
3557 auto wire = hw::WireOp::create(
3558 builder, op.getLoc(), getOrCreateZConstant(resultType), name, innerSym);
3560 if (
auto svAttrs = sv::getSVAttributes(op))
3561 sv::setSVAttributes(wire, svAttrs);
3563 return setLowering(op.getResult(), wire);
3566LogicalResult FIRRTLLowering::visitDecl(VerbatimWireOp op) {
3567 auto resultTy =
lowerType(op.getType());
3570 resultTy = sv::InOutType::get(op.getContext(), resultTy);
3572 SmallVector<Value, 4> operands;
3573 operands.reserve(op.getSubstitutions().size());
3574 for (
auto operand : op.getSubstitutions()) {
3575 auto lowered = getLoweredValue(operand);
3578 operands.push_back(lowered);
3581 ArrayAttr symbols = op.getSymbolsAttr();
3583 symbols = ArrayAttr::get(op.getContext(), {});
3585 return setLoweringTo<sv::VerbatimExprSEOp>(op, resultTy, op.getTextAttr(),
3589LogicalResult FIRRTLLowering::visitDecl(NodeOp op) {
3590 auto operand = getLoweredValue(op.getInput());
3592 return handleZeroBit(op.getInput(), [&]() -> LogicalResult {
3593 if (op.getInnerSym())
3594 return op.emitError(
"zero width node is referenced by name [")
3595 << *op.getInnerSym()
3596 <<
"] (e.g. in an XMR) but must be "
3598 return setLowering(op.getResult(), Value());
3604 auto name = op.getNameAttr();
3605 auto innerSym = lowerInnerSymbol(op);
3608 operand = hw::WireOp::create(builder, operand, name, innerSym);
3611 if (
auto svAttrs = sv::getSVAttributes(op)) {
3613 operand = hw::WireOp::create(builder, operand, name);
3614 sv::setSVAttributes(operand.getDefiningOp(), svAttrs);
3617 return setLowering(op.getResult(), operand);
3620LogicalResult FIRRTLLowering::visitDecl(RegOp op) {
3621 auto resultType =
lowerType(op.getResult().getType());
3624 if (resultType.isInteger(0))
3625 return setLowering(op.getResult(), Value());
3627 Value clockVal = getLoweredValue(op.getClockVal());
3632 auto innerSym = lowerInnerSymbol(op);
3633 Backedge inputEdge = backedgeBuilder.
get(resultType);
3634 auto reg = seq::FirRegOp::create(builder, inputEdge, clockVal,
3635 op.getNameAttr(), innerSym);
3638 if (
auto randomRegister = op->getAttr(
"firrtl.random_init_register"))
3639 reg->setAttr(
"firrtl.random_init_register", randomRegister);
3640 if (
auto randomStart = op->getAttr(
"firrtl.random_init_start"))
3641 reg->setAttr(
"firrtl.random_init_start", randomStart);
3642 if (
auto randomEnd = op->getAttr(
"firrtl.random_init_end"))
3643 reg->setAttr(
"firrtl.random_init_end", randomEnd);
3646 if (
auto svAttrs = sv::getSVAttributes(op))
3647 sv::setSVAttributes(reg, svAttrs);
3650 (void)setLowering(op.getResult(),
reg);
3654LogicalResult FIRRTLLowering::visitDecl(RegResetOp op) {
3655 auto resultType =
lowerType(op.getResult().getType());
3658 if (resultType.isInteger(0))
3659 return setLowering(op.getResult(), Value());
3661 Value clockVal = getLoweredValue(op.getClockVal());
3662 Value resetSignal = getLoweredValue(op.getResetSignal());
3664 Value resetValue = getLoweredAndExtOrTruncValue(
3665 op.getResetValue(), type_cast<FIRRTLBaseType>(op.getResult().getType()));
3667 if (!clockVal || !resetSignal || !resetValue)
3671 auto innerSym = lowerInnerSymbol(op);
3672 bool isAsync = type_isa<AsyncResetType>(op.getResetSignal().getType());
3673 Backedge inputEdge = backedgeBuilder.
get(resultType);
3675 seq::FirRegOp::create(builder, inputEdge, clockVal, op.getNameAttr(),
3676 resetSignal, resetValue, innerSym, isAsync);
3679 if (
auto randomRegister = op->getAttr(
"firrtl.random_init_register"))
3680 reg->setAttr(
"firrtl.random_init_register", randomRegister);
3681 if (
auto randomStart = op->getAttr(
"firrtl.random_init_start"))
3682 reg->setAttr(
"firrtl.random_init_start", randomStart);
3683 if (
auto randomEnd = op->getAttr(
"firrtl.random_init_end"))
3684 reg->setAttr(
"firrtl.random_init_end", randomEnd);
3687 if (
auto svAttrs = sv::getSVAttributes(op))
3688 sv::setSVAttributes(reg, svAttrs);
3691 (void)setLowering(op.getResult(),
reg);
3696LogicalResult FIRRTLLowering::visitDecl(MemOp op) {
3699 if (type_isa<BundleType>(op.getDataType()))
3700 return op.emitOpError(
3701 "should have already been lowered from a ground type to an aggregate "
3702 "type using the LowerTypes pass. Use "
3703 "'firtool --lower-types' or 'circt-opt "
3704 "--pass-pipeline='firrtl.circuit(firrtl-lower-types)' "
3710 auto memType = seq::FirMemType::get(
3713 : std::optional<uint32_t>());
3715 seq::FirMemInitAttr memInit;
3716 if (
auto init = op.getInitAttr())
3717 memInit = seq::FirMemInitAttr::get(init.getContext(), init.getFilename(),
3718 init.getIsBinary(), init.getIsInline());
3720 auto memDecl = seq::FirMemOp::create(
3723 op.getInnerSymAttr(), memInit, op.getPrefixAttr(), Attribute{});
3726 if (
auto file = parent->getAttrOfType<hw::OutputFileAttr>(
"output_file")) {
3728 if (!file.isDirectory())
3729 dir = hw::OutputFileAttr::getAsDirectory(builder.getContext(),
3730 file.getDirectory());
3731 memDecl.setOutputFileAttr(dir);
3737 for (
size_t i = 0, e = op.getNumResults(); i != e; ++i) {
3739 auto addOutput = [&](StringRef field,
size_t width, Value value) {
3742 (void)setLowering(a, value);
3748 auto addInput = [&](StringRef field, Value backedge) {
3750 if (cast<FIRRTLBaseType>(a.getType())
3752 .getBitWidthOrSentinel() > 0)
3753 (void)setLowering(a, backedge);
3759 auto addInputPort = [&](StringRef field,
size_t width) -> Value {
3763 Value backedge, portValue;
3765 portValue = getOrCreateXConstant(1);
3767 auto portType = IntegerType::get(op.getContext(), width);
3768 backedge = portValue = createBackedge(builder.getLoc(), portType);
3770 addInput(field, backedge);
3774 auto addClock = [&](StringRef field) -> Value {
3775 Type clockTy = seq::ClockType::get(op.getContext());
3776 Value portValue = createBackedge(builder.getLoc(), clockTy);
3777 addInput(field, portValue);
3781 auto memportKind = op.getPortKind(i);
3782 if (memportKind == MemOp::PortKind::Read) {
3783 auto addr = addInputPort(
"addr", op.getAddrBits());
3784 auto en = addInputPort(
"en", 1);
3785 auto clk = addClock(
"clk");
3786 auto data = seq::FirMemReadOp::create(builder, memDecl,
addr,
clk,
en);
3788 }
else if (memportKind == MemOp::PortKind::ReadWrite) {
3789 auto addr = addInputPort(
"addr", op.getAddrBits());
3790 auto en = addInputPort(
"en", 1);
3791 auto clk = addClock(
"clk");
3794 auto mode = addInputPort(
"wmode", 1);
3796 mode = builder.createOrFold<
comb::AndOp>(mode, addInputPort(
"wmask", 1),
3803 auto rdata = seq::FirMemReadWriteOp::create(builder, memDecl,
addr,
clk,
3807 auto addr = addInputPort(
"addr", op.getAddrBits());
3810 auto en = addInputPort(
"en", 1);
3814 auto clk = addClock(
"clk");
3827LogicalResult FIRRTLLowering::visitDecl(InstanceOp oldInstance) {
3828 Operation *oldModule =
3829 oldInstance.getReferencedModule(circuitState.getInstanceGraph());
3831 auto *newModule = circuitState.getNewModule(oldModule);
3833 oldInstance->emitOpError(
"could not find module [")
3834 << oldInstance.getModuleName() <<
"] referenced by instance";
3840 ArrayAttr parameters;
3841 if (
auto oldExtModule = dyn_cast<FExtModuleOp>(oldModule))
3846 SmallVector<PortInfo, 8> portInfo = cast<FModuleLike>(oldModule).getPorts();
3851 for (
unsigned portIdx = 0, e = portInfo.size(); portIdx != e; ++portIdx)
3852 portIndicesByName[portInfo[portIdx].name] = portIdx;
3856 SmallVector<Value, 8> operands;
3857 for (
size_t portIndex = 0, e = portInfo.size(); portIndex != e; ++portIndex) {
3858 auto &port = portInfo[portIndex];
3861 oldInstance->emitOpError(
"could not lower type of port ") << port.name;
3866 if (portType.isInteger(0))
3870 if (port.isOutput())
3873 auto portResult = oldInstance.getResult(portIndex);
3874 assert(portResult &&
"invalid IR, couldn't find port");
3878 if (port.isInput()) {
3879 operands.push_back(createBackedge(portResult, portType));
3885 if (type_isa<AnalogType>(portResult.getType()) && portResult.hasOneUse()) {
3886 if (
auto attach = dyn_cast<AttachOp>(*portResult.getUsers().begin())) {
3888 auto loweredResult = getPossiblyInoutLoweredValue(source);
3889 operands.push_back(loweredResult);
3890 (void)setLowering(portResult, loweredResult);
3899 "." + port.getName().str() +
".wire");
3903 (void)setLowering(portResult, wire);
3905 operands.push_back(wire);
3912 auto innerSym = oldInstance.getInnerSymAttr();
3913 if (oldInstance.getLowerToBind()) {
3916 oldInstance.getContext(), oldInstance.getInnerSymAttr(), 0,
3919 auto bindOp = sv::BindOp::create(builder, theModule.getNameAttr(),
3920 innerSym.getSymName());
3923 if (
auto outputFile = oldInstance->getAttr(
"output_file"))
3924 bindOp->setAttr(
"output_file", outputFile);
3927 circuitState.addBind(bindOp);
3932 hw::InstanceOp::create(builder, newModule, oldInstance.getNameAttr(),
3933 operands, parameters, innerSym);
3935 if (oldInstance.getLowerToBind() || oldInstance.getDoNotPrint())
3936 newInstance.setDoNotPrintAttr(builder.getUnitAttr());
3938 if (newInstance.getInnerSymAttr())
3939 if (
auto forceName = circuitState.instanceForceNames.lookup(
3940 {newInstance->getParentOfType<hw::HWModuleOp>().getNameAttr(),
3941 newInstance.getInnerNameAttr()}))
3942 newInstance->setAttr(
"hw.verilogName", forceName);
3946 unsigned resultNo = 0;
3947 for (
size_t portIndex = 0, e = portInfo.size(); portIndex != e; ++portIndex) {
3948 auto &port = portInfo[portIndex];
3952 Value resultVal = newInstance.getResult(resultNo);
3954 auto oldPortResult = oldInstance.getResult(portIndex);
3955 (void)setLowering(oldPortResult, resultVal);
3961LogicalResult FIRRTLLowering::visitDecl(ContractOp oldOp) {
3962 SmallVector<Value> inputs;
3963 SmallVector<Type> types;
3964 for (
auto input : oldOp.getInputs()) {
3965 auto lowered = getLoweredValue(input);
3968 inputs.push_back(lowered);
3969 types.push_back(lowered.getType());
3972 auto newOp = verif::ContractOp::create(builder, types, inputs);
3973 newOp->setDiscardableAttrs(oldOp->getDiscardableAttrDictionary());
3974 auto &body = newOp.getBody().emplaceBlock();
3976 for (
auto [newResult, oldResult, oldArg] :
3977 llvm::zip(newOp.getResults(), oldOp.getResults(),
3978 oldOp.getBody().getArguments())) {
3979 if (failed(setLowering(oldResult, newResult)))
3981 if (failed(setLowering(oldArg, newResult)))
3985 body.getOperations().splice(body.end(),
3986 oldOp.getBody().front().getOperations());
3987 addToWorklist(body);
3997LogicalResult FIRRTLLowering::lowerNoopCast(Operation *op) {
3998 auto operand = getPossiblyInoutLoweredValue(op->getOperand(0));
4003 return setLowering(op->getResult(0), operand);
4006LogicalResult FIRRTLLowering::visitExpr(AsSIntPrimOp op) {
4007 if (isa<ClockType>(op.getInput().getType()))
4008 return setLowering(op->getResult(0),
4009 getLoweredNonClockValue(op.getInput()));
4010 return lowerNoopCast(op);
4013LogicalResult FIRRTLLowering::visitExpr(AsUIntPrimOp op) {
4014 if (isa<ClockType>(op.getInput().getType()))
4015 return setLowering(op->getResult(0),
4016 getLoweredNonClockValue(op.getInput()));
4017 return lowerNoopCast(op);
4020LogicalResult FIRRTLLowering::visitExpr(AsClockPrimOp op) {
4021 return setLoweringTo<seq::ToClockOp>(op, getLoweredValue(op.getInput()));
4024LogicalResult FIRRTLLowering::visitUnrealizedConversionCast(
4025 mlir::UnrealizedConversionCastOp op) {
4027 if (op.getNumOperands() != 1 || op.getNumResults() != 1)
4030 auto operand = op.getOperand(0);
4031 auto result = op.getResult(0);
4034 if (type_isa<FIRRTLType>(operand.getType()) &&
4035 type_isa<FIRRTLType>(result.getType()))
4036 return lowerNoopCast(op);
4040 if (!type_isa<FIRRTLType>(operand.getType())) {
4041 if (type_isa<FIRRTLType>(result.getType()))
4042 return setLowering(result, getPossiblyInoutLoweredValue(operand));
4048 auto loweredResult = getLoweredValue(operand);
4049 if (!loweredResult) {
4052 if (operand.getType().isSignlessInteger(0)) {
4053 return setLowering(result, Value());
4060 result.replaceAllUsesWith(loweredResult);
4064LogicalResult FIRRTLLowering::visitExpr(HWStructCastOp op) {
4067 if (
auto opStructType = dyn_cast<hw::StructType>(op.getOperand().getType()))
4068 return setLowering(op, op.getOperand());
4072 auto result = getLoweredValue(op.getOperand());
4078 op.replaceAllUsesWith(result);
4082LogicalResult FIRRTLLowering::visitExpr(BitCastOp op) {
4083 auto operand = getLoweredValue(op.getOperand());
4086 auto resultType =
lowerType(op.getType());
4090 return setLoweringTo<hw::BitcastOp>(op, resultType, operand);
4093LogicalResult FIRRTLLowering::visitExpr(CvtPrimOp op) {
4094 auto operand = getLoweredValue(op.getOperand());
4098 if (type_cast<IntType>(op.getOperand().getType()).isUnsigned())
4099 return setLowering(op, getOrCreateIntConstant(1, 0));
4101 return setLowering(op, Value());
4106 if (type_cast<IntType>(op.getOperand().getType()).isSigned())
4107 return setLowering(op, operand);
4110 auto zero = getOrCreateIntConstant(1, 0);
4111 return setLoweringTo<comb::ConcatOp>(op, zero, operand);
4114LogicalResult FIRRTLLowering::visitExpr(NotPrimOp op) {
4115 auto operand = getLoweredValue(op.getInput());
4119 auto allOnes = getOrCreateIntConstant(
4120 APInt::getAllOnes(operand.getType().getIntOrFloatBitWidth()));
4121 return setLoweringTo<comb::XorOp>(op, operand, allOnes,
true);
4124LogicalResult FIRRTLLowering::visitExpr(NegPrimOp op) {
4127 auto operand = getLoweredAndExtendedValue(op.getInput(), op.getType());
4131 auto resultType =
lowerType(op.getType());
4133 auto zero = getOrCreateIntConstant(resultType.getIntOrFloatBitWidth(), 0);
4134 return setLoweringTo<comb::SubOp>(op, zero, operand,
true);
4138LogicalResult FIRRTLLowering::visitExpr(PadPrimOp op) {
4139 auto operand = getLoweredAndExtendedValue(op.getInput(), op.getType());
4142 return setLowering(op, operand);
4145LogicalResult FIRRTLLowering::visitExpr(XorRPrimOp op) {
4146 auto operand = getLoweredValue(op.getInput());
4149 return setLowering(op, getOrCreateIntConstant(1, 0));
4154 return setLoweringTo<comb::ParityOp>(op, builder.getIntegerType(1), operand,
4158LogicalResult FIRRTLLowering::visitExpr(AndRPrimOp op) {
4159 auto operand = getLoweredValue(op.getInput());
4162 return setLowering(op, getOrCreateIntConstant(1, 1));
4167 return setLoweringTo<comb::ICmpOp>(
4168 op, ICmpPredicate::eq, operand,
4169 getOrCreateIntConstant(
4170 APInt::getAllOnes(operand.getType().getIntOrFloatBitWidth())),
4174LogicalResult FIRRTLLowering::visitExpr(OrRPrimOp op) {
4175 auto operand = getLoweredValue(op.getInput());
4178 return setLowering(op, getOrCreateIntConstant(1, 0));
4184 return setLoweringTo<comb::ICmpOp>(
4185 op, ICmpPredicate::ne, operand,
4186 getOrCreateIntConstant(operand.getType().getIntOrFloatBitWidth(), 0),
4194template <
typename ResultOpType>
4195LogicalResult FIRRTLLowering::lowerBinOpToVariadic(Operation *op) {
4196 auto resultType = op->getResult(0).getType();
4197 auto lhs = getLoweredAndExtendedValue(op->getOperand(0), resultType);
4198 auto rhs = getLoweredAndExtendedValue(op->getOperand(1), resultType);
4202 return setLoweringTo<ResultOpType>(op, lhs, rhs,
true);
4208template <
typename ResultOpType>
4209LogicalResult FIRRTLLowering::lowerElementwiseLogicalOp(Operation *op) {
4210 auto resultType = op->getResult(0).getType();
4211 auto lhs = getLoweredAndExtendedValue(op->getOperand(0), resultType);
4212 auto rhs = getLoweredAndExtendedValue(op->getOperand(1), resultType);
4223 auto intType = builder.getIntegerType(*bitwidth);
4224 auto retType = lhs.getType();
4227 auto result = builder.createOrFold<ResultOpType>(lhs, rhs,
true);
4228 return setLoweringTo<hw::BitcastOp>(op, retType, result);
4233template <
typename ResultUn
signedOpType,
typename ResultSignedOpType>
4234LogicalResult FIRRTLLowering::lowerBinOp(Operation *op) {
4236 auto resultType = op->getResult(0).getType();
4237 auto lhs = getLoweredAndExtendedValue(op->getOperand(0), resultType);
4238 auto rhs = getLoweredAndExtendedValue(op->getOperand(1), resultType);
4243 if (type_cast<IntType>(resultType).isSigned())
4244 return setLoweringTo<ResultSignedOpType>(op, lhs, rhs,
true);
4245 return setLoweringTo<ResultUnsignedOpType>(op, lhs, rhs,
true);
4250LogicalResult FIRRTLLowering::lowerCmpOp(Operation *op, ICmpPredicate signedOp,
4251 ICmpPredicate unsignedOp) {
4253 auto lhsIntType = type_cast<IntType>(op->getOperand(0).getType());
4254 auto rhsIntType = type_cast<IntType>(op->getOperand(1).getType());
4255 if (!lhsIntType.hasWidth() || !rhsIntType.hasWidth())
4259 if (cmpType.getWidth() == 0)
4260 cmpType = UIntType::get(builder.getContext(), 1);
4261 auto lhs = getLoweredAndExtendedValue(op->getOperand(0), cmpType);
4262 auto rhs = getLoweredAndExtendedValue(op->getOperand(1), cmpType);
4267 Type resultType = builder.getIntegerType(1);
4268 return setLoweringTo<comb::ICmpOp>(
4269 op, resultType, lhsIntType.isSigned() ? signedOp : unsignedOp, lhs, rhs,
4275template <
typename SignedOp,
typename Un
signedOp>
4276LogicalResult FIRRTLLowering::lowerDivLikeOp(Operation *op) {
4280 auto opType = type_cast<IntType>(op->getResult(0).getType());
4281 if (opType.getWidth() == 0)
4282 return setLowering(op->getResult(0), Value());
4286 auto lhs = getLoweredAndExtendedValue(op->getOperand(0), resultType);
4287 auto rhs = getLoweredAndExtendedValue(op->getOperand(1), resultType);
4292 if (opType.isSigned())
4293 result = builder.createOrFold<SignedOp>(lhs, rhs,
true);
4295 result = builder.createOrFold<UnsignedOp>(lhs, rhs,
true);
4297 if (
auto *definingOp = result.getDefiningOp())
4300 if (resultType == opType)
4301 return setLowering(op->getResult(0), result);
4302 return setLoweringTo<comb::ExtractOp>(op,
lowerType(opType), result, 0);
4305LogicalResult FIRRTLLowering::visitExpr(CatPrimOp op) {
4307 if (op.getInputs().empty())
4308 return setLowering(op, Value());
4310 SmallVector<Value> loweredOperands;
4313 for (
auto operand : op.getInputs()) {
4314 auto loweredOperand = getLoweredValue(operand);
4315 if (loweredOperand) {
4316 loweredOperands.push_back(loweredOperand);
4319 auto result =
handleZeroBit(operand, [&]() {
return success(); });
4327 if (loweredOperands.empty())
4328 return setLowering(op, Value());
4331 return setLoweringTo<comb::ConcatOp>(op, loweredOperands);
4338LogicalResult FIRRTLLowering::visitExpr(IsXIntrinsicOp op) {
4339 auto input = getLoweredNonClockValue(op.getArg());
4343 if (!isa<IntType>(input.getType())) {
4344 auto srcType = op.getArg().getType();
4346 assert(bitwidth &&
"Unknown width");
4347 auto intType = builder.getIntegerType(*bitwidth);
4348 input = builder.createOrFold<
hw::BitcastOp>(intType, input);
4351 return setLoweringTo<comb::ICmpOp>(
4352 op, ICmpPredicate::ceq, input,
4353 getOrCreateXConstant(input.getType().getIntOrFloatBitWidth()),
true);
4356LogicalResult FIRRTLLowering::visitStmt(FPGAProbeIntrinsicOp op) {
4357 auto operand = getLoweredValue(op.getInput());
4358 hw::WireOp::create(builder, operand);
4362LogicalResult FIRRTLLowering::visitExpr(PlusArgsTestIntrinsicOp op) {
4363 return setLoweringTo<sim::PlusArgsTestOp>(op, builder.getIntegerType(1),
4364 op.getFormatStringAttr());
4367LogicalResult FIRRTLLowering::visitExpr(PlusArgsValueIntrinsicOp op) {
4368 auto type =
lowerType(op.getResult().getType());
4372 auto valueOp = sim::PlusArgsValueOp::create(
4373 builder, builder.getIntegerType(1), type, op.getFormatStringAttr());
4374 if (failed(setLowering(op.getResult(), valueOp.getResult())))
4376 if (failed(setLowering(op.getFound(), valueOp.getFound())))
4381LogicalResult FIRRTLLowering::visitExpr(SizeOfIntrinsicOp op) {
4382 op.emitError(
"SizeOf should have been resolved.");
4386LogicalResult FIRRTLLowering::visitExpr(ClockGateIntrinsicOp op) {
4388 if (op.getTestEnable())
4389 testEnable = getLoweredValue(op.getTestEnable());
4390 return setLoweringTo<seq::ClockGateOp>(
4391 op, getLoweredValue(op.getInput()), getLoweredValue(op.getEnable()),
4392 testEnable, hw::InnerSymAttr{});
4395LogicalResult FIRRTLLowering::visitExpr(ClockInverterIntrinsicOp op) {
4396 auto operand = getLoweredValue(op.getInput());
4397 return setLoweringTo<seq::ClockInverterOp>(op, operand);
4400LogicalResult FIRRTLLowering::visitExpr(ClockDividerIntrinsicOp op) {
4401 auto operand = getLoweredValue(op.getInput());
4402 return setLoweringTo<seq::ClockDividerOp>(op, operand, op.getPow2());
4405LogicalResult FIRRTLLowering::visitExpr(LTLAndIntrinsicOp op) {
4406 return setLoweringToLTL<ltl::AndOp>(
4408 ValueRange{getLoweredValue(op.getLhs()), getLoweredValue(op.getRhs())});
4411LogicalResult FIRRTLLowering::visitExpr(LTLOrIntrinsicOp op) {
4412 return setLoweringToLTL<ltl::OrOp>(
4414 ValueRange{getLoweredValue(op.getLhs()), getLoweredValue(op.getRhs())});
4417LogicalResult FIRRTLLowering::visitExpr(LTLIntersectIntrinsicOp op) {
4418 return setLoweringToLTL<ltl::IntersectOp>(
4420 ValueRange{getLoweredValue(op.getLhs()), getLoweredValue(op.getRhs())});
4423LogicalResult FIRRTLLowering::visitExpr(LTLDelayIntrinsicOp op) {
4424 return setLoweringToLTL<ltl::DelayOp>(op, getLoweredValue(op.getInput()),
4425 op.getDelayAttr(), op.getLengthAttr());
4428LogicalResult FIRRTLLowering::visitExpr(LTLConcatIntrinsicOp op) {
4429 return setLoweringToLTL<ltl::ConcatOp>(
4431 ValueRange{getLoweredValue(op.getLhs()), getLoweredValue(op.getRhs())});
4434LogicalResult FIRRTLLowering::visitExpr(LTLRepeatIntrinsicOp op) {
4435 return setLoweringToLTL<ltl::RepeatOp>(op, getLoweredValue(op.getInput()),
4436 op.getBaseAttr(), op.getMoreAttr());
4439LogicalResult FIRRTLLowering::visitExpr(LTLGoToRepeatIntrinsicOp op) {
4440 return setLoweringToLTL<ltl::GoToRepeatOp>(
4441 op, getLoweredValue(op.getInput()), op.getBaseAttr(), op.getMoreAttr());
4444LogicalResult FIRRTLLowering::visitExpr(LTLNonConsecutiveRepeatIntrinsicOp op) {
4445 return setLoweringToLTL<ltl::NonConsecutiveRepeatOp>(
4446 op, getLoweredValue(op.getInput()), op.getBaseAttr(), op.getMoreAttr());
4449LogicalResult FIRRTLLowering::visitExpr(LTLNotIntrinsicOp op) {
4450 return setLoweringToLTL<ltl::NotOp>(op, getLoweredValue(op.getInput()));
4453LogicalResult FIRRTLLowering::visitExpr(LTLImplicationIntrinsicOp op) {
4454 return setLoweringToLTL<ltl::ImplicationOp>(
4456 ValueRange{getLoweredValue(op.getLhs()), getLoweredValue(op.getRhs())});
4459LogicalResult FIRRTLLowering::visitExpr(LTLUntilIntrinsicOp op) {
4460 return setLoweringToLTL<ltl::UntilOp>(
4462 ValueRange{getLoweredValue(op.getLhs()), getLoweredValue(op.getRhs())});
4465LogicalResult FIRRTLLowering::visitExpr(LTLEventuallyIntrinsicOp op) {
4466 return setLoweringToLTL<ltl::EventuallyOp>(op,
4467 getLoweredValue(op.getInput()));
4470LogicalResult FIRRTLLowering::visitExpr(LTLClockIntrinsicOp op) {
4471 return setLoweringToLTL<ltl::ClockOp>(op, getLoweredValue(op.getInput()),
4472 ltl::ClockEdge::Pos,
4473 getLoweredNonClockValue(op.getClock()));
4476template <
typename TargetOp,
typename IntrinsicOp>
4477LogicalResult FIRRTLLowering::lowerVerifIntrinsicOp(IntrinsicOp op) {
4478 auto property = getLoweredValue(op.getProperty());
4479 auto enable = op.getEnable() ? getLoweredValue(op.getEnable()) : Value();
4480 TargetOp::create(builder, property, enable, op.getLabelAttr());
4484LogicalResult FIRRTLLowering::visitStmt(VerifAssertIntrinsicOp op) {
4485 return lowerVerifIntrinsicOp<verif::AssertOp>(op);
4488LogicalResult FIRRTLLowering::visitStmt(VerifAssumeIntrinsicOp op) {
4489 return lowerVerifIntrinsicOp<verif::AssumeOp>(op);
4492LogicalResult FIRRTLLowering::visitStmt(VerifCoverIntrinsicOp op) {
4493 return lowerVerifIntrinsicOp<verif::CoverOp>(op);
4496LogicalResult FIRRTLLowering::visitStmt(VerifRequireIntrinsicOp op) {
4497 if (!isa<verif::ContractOp>(op->getParentOp()))
4498 return lowerVerifIntrinsicOp<verif::AssertOp>(op);
4499 return lowerVerifIntrinsicOp<verif::RequireOp>(op);
4502LogicalResult FIRRTLLowering::visitStmt(VerifEnsureIntrinsicOp op) {
4503 if (!isa<verif::ContractOp>(op->getParentOp()))
4504 return lowerVerifIntrinsicOp<verif::AssertOp>(op);
4505 return lowerVerifIntrinsicOp<verif::EnsureOp>(op);
4508LogicalResult FIRRTLLowering::visitExpr(HasBeenResetIntrinsicOp op) {
4509 auto clock = getLoweredNonClockValue(op.getClock());
4510 auto reset = getLoweredValue(op.getReset());
4511 if (!clock || !reset)
4513 auto resetType = op.getReset().getType();
4514 auto uintResetType = dyn_cast<UIntType>(resetType);
4515 auto isSync = uintResetType && uintResetType.getWidth() == 1;
4516 auto isAsync = isa<AsyncResetType>(resetType);
4517 if (!isAsync && !isSync) {
4518 auto d = op.emitError(
"uninferred reset passed to 'has_been_reset'; "
4519 "requires sync or async reset");
4520 d.attachNote() <<
"reset is of type " << resetType
4521 <<
", should be '!firrtl.uint<1>' or '!firrtl.asyncreset'";
4524 return setLoweringTo<verif::HasBeenResetOp>(op, clock, reset, isAsync);
4531LogicalResult FIRRTLLowering::visitExpr(BitsPrimOp op) {
4532 auto input = getLoweredValue(op.getInput());
4536 Type resultType = builder.getIntegerType(op.getHi() - op.getLo() + 1);
4537 return setLoweringTo<comb::ExtractOp>(op, resultType, input, op.getLo());
4540LogicalResult FIRRTLLowering::visitExpr(InvalidValueOp op) {
4541 auto resultTy =
lowerType(op.getType());
4548 if (type_isa<AnalogType>(op.getType()))
4551 return setLoweringTo<sv::WireOp>(op, resultTy,
".invalid_analog");
4554 if (type_cast<FIRRTLBaseType>(op.getType()).containsAnalog())
4565 auto constant = getOrCreateIntConstant(*bitwidth, 0);
4567 if (!type_isa<IntegerType>(resultTy))
4569 return setLowering(op, constant);
4573 op.emitOpError(
"unsupported type");
4577LogicalResult FIRRTLLowering::visitExpr(HeadPrimOp op) {
4578 auto input = getLoweredValue(op.getInput());
4581 auto inWidth = type_cast<IntegerType>(input.getType()).getWidth();
4582 if (op.getAmount() == 0)
4583 return setLowering(op, Value());
4584 Type resultType = builder.getIntegerType(op.getAmount());
4585 return setLoweringTo<comb::ExtractOp>(op, resultType, input,
4586 inWidth - op.getAmount());
4589LogicalResult FIRRTLLowering::visitExpr(ShlPrimOp op) {
4590 auto input = getLoweredValue(op.getInput());
4593 if (op.getAmount() == 0)
4595 return setLowering(op, getOrCreateIntConstant(op.getAmount(), 0));
4600 if (op.getAmount() == 0)
4601 return setLowering(op, input);
4603 auto zero = getOrCreateIntConstant(op.getAmount(), 0);
4604 return setLoweringTo<comb::ConcatOp>(op, input, zero);
4607LogicalResult FIRRTLLowering::visitExpr(ShrPrimOp op) {
4608 auto input = getLoweredValue(op.getInput());
4613 auto inWidth = type_cast<IntegerType>(input.getType()).getWidth();
4614 auto shiftAmount = op.getAmount();
4615 if (shiftAmount >= inWidth) {
4617 if (type_cast<IntType>(op.getInput().getType()).isUnsigned())
4618 return setLowering(op, {});
4621 shiftAmount = inWidth - 1;
4624 Type resultType = builder.getIntegerType(inWidth - shiftAmount);
4625 return setLoweringTo<comb::ExtractOp>(op, resultType, input, shiftAmount);
4628LogicalResult FIRRTLLowering::visitExpr(TailPrimOp op) {
4629 auto input = getLoweredValue(op.getInput());
4633 auto inWidth = type_cast<IntegerType>(input.getType()).getWidth();
4634 if (inWidth == op.getAmount())
4635 return setLowering(op, Value());
4636 Type resultType = builder.getIntegerType(inWidth - op.getAmount());
4637 return setLoweringTo<comb::ExtractOp>(op, resultType, input, 0);
4640LogicalResult FIRRTLLowering::visitExpr(MuxPrimOp op) {
4641 auto cond = getLoweredValue(op.getSel());
4642 auto ifTrue = getLoweredAndExtendedValue(op.getHigh(), op.getType());
4643 auto ifFalse = getLoweredAndExtendedValue(op.getLow(), op.getType());
4644 if (!cond || !ifTrue || !ifFalse)
4647 if (isa<ClockType>(op.getType()))
4648 return setLoweringTo<seq::ClockMuxOp>(op, cond, ifTrue, ifFalse);
4649 return setLoweringTo<comb::MuxOp>(op, ifTrue.getType(), cond, ifTrue, ifFalse,
4653LogicalResult FIRRTLLowering::visitExpr(Mux2CellIntrinsicOp op) {
4654 auto cond = getLoweredValue(op.getSel());
4655 auto ifTrue = getLoweredAndExtendedValue(op.getHigh(), op.getType());
4656 auto ifFalse = getLoweredAndExtendedValue(op.getLow(), op.getType());
4657 if (!cond || !ifTrue || !ifFalse)
4660 auto val = comb::MuxOp::create(builder, ifTrue.getType(), cond, ifTrue,
4662 return setLowering(op, createValueWithMuxAnnotation(val,
true));
4665LogicalResult FIRRTLLowering::visitExpr(Mux4CellIntrinsicOp op) {
4666 auto sel = getLoweredValue(op.getSel());
4667 auto v3 = getLoweredAndExtendedValue(op.getV3(), op.getType());
4668 auto v2 = getLoweredAndExtendedValue(op.getV2(), op.getType());
4669 auto v1 = getLoweredAndExtendedValue(op.getV1(), op.getType());
4670 auto v0 = getLoweredAndExtendedValue(op.getV0(), op.getType());
4671 if (!sel || !v3 || !v2 || !v1 || !v0)
4673 Value array[] = {v3, v2, v1, v0};
4676 return setLowering(op, createValueWithMuxAnnotation(val,
false));
4695Value FIRRTLLowering::createValueWithMuxAnnotation(Operation *op,
bool isMux2) {
4696 assert(op->getNumResults() == 1 &&
"only expect a single result");
4697 auto val = op->getResult(0);
4701 op, sv::SVAttributeAttr::get(builder.getContext(),
"cadence map_to_mux",
4708 OpBuilder::InsertionGuard guard(builder);
4709 builder.setInsertionPoint(op);
4710 StringRef namehint = isMux2 ?
"mux2cell_in" :
"mux4cell_in";
4711 for (
auto [idx, operand] :
llvm::enumerate(op->getOperands())) {
4713 op->getContext(),
nullptr, 0,
4716 hw::WireOp::create(builder, operand, namehint + Twine(idx), innerSym);
4717 op->setOperand(idx, wire);
4722 sv::setSVAttributes(assignOp,
4723 sv::SVAttributeAttr::get(builder.getContext(),
4724 "synopsys infer_mux_override",
4729Value FIRRTLLowering::createArrayIndexing(Value array, Value index) {
4731 auto size = hw::type_cast<hw::ArrayType>(array.getType()).getNumElements();
4736 if (!llvm::isPowerOf2_64(size)) {
4737 auto extElem = getOrCreateIntConstant(APInt(llvm::Log2_64_Ceil(size), 0));
4739 SmallVector<Value> temp(llvm::NextPowerOf2(size) - size, extValue);
4741 Value temp2[] = {ext.getResult(), array};
4747 return inBoundsRead;
4750LogicalResult FIRRTLLowering::visitExpr(MultibitMuxOp op) {
4752 auto index = getLoweredAndExtOrTruncValue(
4754 UIntType::get(op.getContext(),
4759 SmallVector<Value> loweredInputs;
4760 loweredInputs.reserve(op.getInputs().size());
4761 for (
auto input : op.getInputs()) {
4762 auto lowered = getLoweredAndExtendedValue(input, op.getType());
4765 loweredInputs.push_back(lowered);
4769 return setLowering(op, createArrayIndexing(array, index));
4772LogicalResult FIRRTLLowering::visitExpr(VerbatimExprOp op) {
4773 auto resultTy =
lowerType(op.getType());
4777 SmallVector<Value, 4> operands;
4778 operands.reserve(op.getSubstitutions().size());
4779 for (
auto operand : op.getSubstitutions()) {
4780 auto lowered = getLoweredValue(operand);
4783 operands.push_back(lowered);
4786 ArrayAttr symbols = op.getSymbolsAttr();
4788 symbols = ArrayAttr::get(op.getContext(), {});
4790 return setLoweringTo<sv::VerbatimExprOp>(op, resultTy, op.getTextAttr(),
4794LogicalResult FIRRTLLowering::visitExpr(XMRRefOp op) {
4798 Type baseType = op.getType().getType();
4801 if (isa<ClockType>(baseType))
4802 xmrType = builder.getIntegerType(1);
4806 return setLoweringTo<sv::XMRRefOp>(op, sv::InOutType::get(xmrType),
4807 op.getRef(), op.getVerbatimSuffixAttr());
4810LogicalResult FIRRTLLowering::visitExpr(XMRDerefOp op) {
4814 if (isa<ClockType>(op.getType()))
4815 xmrType = builder.getIntegerType(1);
4819 auto xmr = sv::XMRRefOp::create(builder, sv::InOutType::get(xmrType),
4820 op.getRef(), op.getVerbatimSuffixAttr());
4821 auto readXmr = getReadValue(xmr);
4822 if (!isa<ClockType>(op.getType()))
4823 return setLowering(op, readXmr);
4824 return setLoweringTo<seq::ToClockOp>(op, readXmr);
4829LogicalResult FIRRTLLowering::visitExpr(TimeOp op) {
return success(); }
4830LogicalResult FIRRTLLowering::visitExpr(HierarchicalModuleNameOp op) {
4838LogicalResult FIRRTLLowering::visitStmt(SkipOp op) {
4850FailureOr<bool> FIRRTLLowering::lowerConnect(Value destVal, Value srcVal) {
4851 auto srcType = srcVal.getType();
4852 auto dstType = destVal.getType();
4853 if (srcType != dstType &&
4854 (isa<hw::TypeAliasType>(srcType) || isa<hw::TypeAliasType>(dstType))) {
4857 return TypeSwitch<Operation *, FailureOr<bool>>(destVal.getDefiningOp())
4858 .Case<hw::WireOp>([&](
auto op) {
4859 maybeUnused(op.getInput());
4860 op.getInputMutable().assign(srcVal);
4863 .Case<seq::FirRegOp>([&](
auto op) {
4864 maybeUnused(op.getNext());
4865 op.getNextMutable().assign(srcVal);
4868 .Case<hw::StructExtractOp, hw::ArrayGetOp>([](
auto op) {
4871 op.emitOpError(
"used as connect destination");
4874 .Default([](
auto) {
return false; });
4877LogicalResult FIRRTLLowering::visitStmt(ConnectOp op) {
4878 auto dest = op.getDest();
4880 auto destType = type_cast<FIRRTLBaseType>(dest.getType()).getPassiveType();
4881 auto srcVal = getLoweredAndExtendedValue(op.getSrc(), destType);
4883 return handleZeroBit(op.getSrc(), []() { return success(); });
4885 auto destVal = getPossiblyInoutLoweredValue(dest);
4889 auto result = lowerConnect(destVal, srcVal);
4897 if (updateIfBackedge(destVal, srcVal))
4900 if (!isa<hw::InOutType>(destVal.getType()))
4901 return op.emitError(
"destination isn't an inout type");
4907LogicalResult FIRRTLLowering::visitStmt(MatchingConnectOp op) {
4908 auto dest = op.getDest();
4909 auto srcVal = getLoweredValue(op.getSrc());
4911 return handleZeroBit(op.getSrc(), []() { return success(); });
4913 auto destVal = getPossiblyInoutLoweredValue(dest);
4917 auto result = lowerConnect(destVal, srcVal);
4925 if (updateIfBackedge(destVal, srcVal))
4928 if (!isa<hw::InOutType>(destVal.getType()))
4929 return op.emitError(
"destination isn't an inout type");
4935LogicalResult FIRRTLLowering::visitStmt(ForceOp op) {
4936 auto srcVal = getLoweredValue(op.getSrc());
4940 auto destVal = getPossiblyInoutLoweredValue(op.getDest());
4944 if (!isa<hw::InOutType>(destVal.getType()))
4945 return op.emitError(
"destination isn't an inout type");
4948 circuitState.addMacroDecl(builder.getStringAttr(
"SYNTHESIS"));
4949 addToIfDefBlock(
"SYNTHESIS", std::function<
void()>(), [&]() {
4950 addToInitialBlock([&]() { sv::ForceOp::create(builder, destVal, srcVal); });
4955LogicalResult FIRRTLLowering::visitStmt(RefForceOp op) {
4956 auto src = getLoweredNonClockValue(op.getSrc());
4957 auto clock = getLoweredNonClockValue(op.getClock());
4958 auto pred = getLoweredValue(op.getPredicate());
4959 if (!src || !clock || !pred)
4962 auto destVal = getPossiblyInoutLoweredValue(op.getDest());
4967 circuitState.addMacroDecl(builder.getStringAttr(
"SYNTHESIS"));
4968 addToIfDefBlock(
"SYNTHESIS", std::function<
void()>(), [&]() {
4969 addToAlwaysBlock(clock, [&]() {
4970 addIfProceduralBlock(
4971 pred, [&]() { sv::ForceOp::create(builder, destVal, src); });
4976LogicalResult FIRRTLLowering::visitStmt(RefForceInitialOp op) {
4977 auto src = getLoweredNonClockValue(op.getSrc());
4978 auto pred = getLoweredValue(op.getPredicate());
4982 auto destVal = getPossiblyInoutLoweredValue(op.getDest());
4987 circuitState.addMacroDecl(builder.getStringAttr(
"SYNTHESIS"));
4988 addToIfDefBlock(
"SYNTHESIS", std::function<
void()>(), [&]() {
4989 addToInitialBlock([&]() {
4990 addIfProceduralBlock(
4991 pred, [&]() { sv::ForceOp::create(builder, destVal, src); });
4996LogicalResult FIRRTLLowering::visitStmt(RefReleaseOp op) {
4997 auto clock = getLoweredNonClockValue(op.getClock());
4998 auto pred = getLoweredValue(op.getPredicate());
4999 if (!clock || !pred)
5002 auto destVal = getPossiblyInoutLoweredValue(op.getDest());
5007 circuitState.addMacroDecl(builder.getStringAttr(
"SYNTHESIS"));
5008 addToIfDefBlock(
"SYNTHESIS", std::function<
void()>(), [&]() {
5009 addToAlwaysBlock(clock, [&]() {
5010 addIfProceduralBlock(pred,
5011 [&]() { sv::ReleaseOp::create(builder, destVal); });
5016LogicalResult FIRRTLLowering::visitStmt(RefReleaseInitialOp op) {
5017 auto destVal = getPossiblyInoutLoweredValue(op.getDest());
5018 auto pred = getLoweredValue(op.getPredicate());
5019 if (!destVal || !pred)
5023 circuitState.addMacroDecl(builder.getStringAttr(
"SYNTHESIS"));
5024 addToIfDefBlock(
"SYNTHESIS", std::function<
void()>(), [&]() {
5025 addToInitialBlock([&]() {
5026 addIfProceduralBlock(pred,
5027 [&]() { sv::ReleaseOp::create(builder, destVal); });
5035 StringRef originalFormatString,
5036 ValueRange operands,
5037 StringAttr &result) {
5040 SmallString<32> formatString;
5041 for (
size_t i = 0, e = originalFormatString.size(), subIdx = 0; i != e; ++i) {
5042 char c = originalFormatString[i];
5046 formatString.push_back(c);
5049 SmallString<6> width;
5050 c = originalFormatString[++i];
5053 c = originalFormatString[++i];
5064 formatString.append(width);
5070 formatString.push_back(c);
5077 if (originalFormatString.slice(i, i + 4) !=
"{{}}") {
5078 formatString.push_back(c);
5082 auto substitution = operands[subIdx++];
5083 assert(type_isa<FStringType>(substitution.getType()) &&
5084 "the operand for a '{{}}' substitution must be an 'fstring' type");
5086 TypeSwitch<Operation *, LogicalResult>(substitution.getDefiningOp())
5087 .template Case<TimeOp>([&](
auto) {
5088 formatString.append(
"%0t");
5091 .
template Case<HierarchicalModuleNameOp>([&](
auto) {
5092 formatString.append(
"%m");
5095 .Default([&](
auto) {
5096 emitError(loc,
"has a substitution with an unimplemented "
5098 .attachNote(substitution.getLoc())
5099 <<
"op with an unimplemented lowering is here";
5109 formatString.push_back(c);
5113 result = StringAttr::get(loc->getContext(), formatString);
5120LogicalResult FIRRTLLowering::visitPrintfLike(
5121 T op,
const FileDescriptorInfo &fileDescriptorInfo,
bool usePrintfCond) {
5122 auto clock = getLoweredNonClockValue(op.getClock());
5123 auto cond = getLoweredValue(op.getCond());
5124 if (!clock || !cond)
5127 StringAttr formatString;
5129 op.getSubstitutions(), formatString)))
5132 auto fn = [&](Value fd) {
5133 SmallVector<Value> operands;
5134 if (failed(loweredFmtOperands(op.getSubstitutions(), operands)))
5136 sv::FWriteOp::create(builder, op.getLoc(), fd, formatString, operands);
5140 return lowerStatementWithFd(fileDescriptorInfo, clock, cond, fn,
5144LogicalResult FIRRTLLowering::visitStmt(FPrintFOp op) {
5145 StringAttr outputFileAttr;
5147 op.getOutputFileSubstitutions(),
5151 FileDescriptorInfo outputFile(outputFileAttr,
5152 op.getOutputFileSubstitutions());
5153 return visitPrintfLike(op, outputFile,
false);
5157LogicalResult FIRRTLLowering::visitStmt(FFlushOp op) {
5158 auto clock = getLoweredNonClockValue(op.getClock());
5159 auto cond = getLoweredValue(op.getCond());
5160 if (!clock || !cond)
5163 auto fn = [&](Value fd) {
5164 sv::FFlushOp::create(builder, op.getLoc(), fd);
5168 if (!op.getOutputFileAttr())
5169 return lowerStatementWithFd({}, clock, cond, fn,
false);
5173 StringAttr outputFileAttr;
5175 op.getOutputFileSubstitutions(),
5179 return lowerStatementWithFd(
5180 FileDescriptorInfo(outputFileAttr, op.getOutputFileSubstitutions()),
5181 clock, cond, fn,
false);
5186LogicalResult FIRRTLLowering::visitStmt(StopOp op) {
5187 auto clock = getLoweredValue(op.getClock());
5188 auto cond = getLoweredValue(op.getCond());
5189 if (!clock || !cond)
5192 circuitState.usedStopCond =
true;
5193 circuitState.addFragment(theModule,
"STOP_COND_FRAGMENT");
5196 sv::MacroRefExprOp::create(builder, cond.getType(),
"STOP_COND_");
5197 Value exitCond = builder.createOrFold<
comb::AndOp>(stopCond, cond,
true);
5199 sim::ClockedTerminateOp::create(builder, clock, exitCond,
5200 op.getExitCode() == 0,
5209template <
typename... Args>
5211 StringRef opName, Args &&...args) {
5212 if (opName ==
"assert")
5213 return sv::AssertOp::create(builder, std::forward<Args>(args)...);
5214 if (opName ==
"assume")
5215 return sv::AssumeOp::create(builder, std::forward<Args>(args)...);
5216 if (opName ==
"cover")
5217 return sv::CoverOp::create(builder, std::forward<Args>(args)...);
5218 llvm_unreachable(
"unknown verification op");
5224template <
typename... Args>
5226 StringRef opName, Args &&...args) {
5227 if (opName ==
"assert")
5228 return sv::AssertConcurrentOp::create(builder, std::forward<Args>(args)...);
5229 if (opName ==
"assume")
5230 return sv::AssumeConcurrentOp::create(builder, std::forward<Args>(args)...);
5231 if (opName ==
"cover")
5232 return sv::CoverConcurrentOp::create(builder, std::forward<Args>(args)...);
5233 llvm_unreachable(
"unknown verification op");
5254LogicalResult FIRRTLLowering::lowerVerificationStatement(
5255 Operation *op, StringRef labelPrefix, Value opClock, Value opPredicate,
5256 Value opEnable, StringAttr opMessageAttr, ValueRange opOperands,
5257 StringAttr opNameAttr,
bool isConcurrent, EventControl opEventControl) {
5258 StringRef opName = op->getName().stripDialect();
5261 ArrayRef<Attribute> guards{};
5262 if (
auto guardsAttr = op->template getAttrOfType<ArrayAttr>(
"guards"))
5263 guards = guardsAttr.getValue();
5265 auto isCover = isa<CoverOp>(op);
5266 auto clock = getLoweredNonClockValue(opClock);
5267 auto enable = getLoweredValue(opEnable);
5268 auto predicate = getLoweredValue(opPredicate);
5269 if (!clock || !enable || !predicate)
5273 if (opNameAttr && !opNameAttr.getValue().empty())
5275 StringAttr prefixedLabel;
5278 StringAttr::get(builder.getContext(), labelPrefix + label.getValue());
5281 SmallVector<Value> messageOps;
5285 if (flavor == VerificationFlavor::IfElseFatal && !isa<AssertOp>(op))
5286 flavor = VerificationFlavor::None;
5288 if (flavor == VerificationFlavor::None) {
5292 auto format = op->getAttrOfType<StringAttr>(
"format");
5294 if (isConcurrent && format && format.getValue() ==
"ifElseFatal") {
5295 if (!isa<AssertOp>(op))
5296 return op->emitError()
5297 <<
"ifElseFatal format cannot be used for non-assertions";
5298 flavor = VerificationFlavor::IfElseFatal;
5299 }
else if (isConcurrent)
5300 flavor = VerificationFlavor::SVA;
5302 flavor = VerificationFlavor::Immediate;
5305 if (!isCover && opMessageAttr && !opMessageAttr.getValue().empty()) {
5309 opOperands, message)))
5312 if (failed(loweredFmtOperands(opOperands, messageOps)))
5315 if (flavor == VerificationFlavor::SVA) {
5320 for (
auto &loweredValue : messageOps)
5321 loweredValue =
sv::SampledOp::create(builder, loweredValue);
5327 case VerificationFlavor::Immediate: {
5329 auto deferImmediate = circt::sv::DeferAssertAttr::get(
5330 builder.getContext(), circt::sv::DeferAssert::Immediate);
5331 addToAlwaysBlock(clock, [&]() {
5332 addIfProceduralBlock(enable, [&]() {
5334 prefixedLabel, message, messageOps);
5339 case VerificationFlavor::IfElseFatal: {
5340 assert(isa<AssertOp>(op) &&
"only assert is expected");
5343 auto boolType = IntegerType::get(builder.getContext(), 1);
5344 predicate = comb::createOrFoldNot(predicate, builder,
true);
5345 predicate = builder.createOrFold<
comb::AndOp>(enable, predicate,
true);
5347 circuitState.addMacroDecl(builder.getStringAttr(
"SYNTHESIS"));
5348 addToIfDefBlock(
"SYNTHESIS", {}, [&]() {
5349 addToAlwaysBlock(clock, [&]() {
5350 addIfProceduralBlock(predicate, [&]() {
5351 circuitState.usedStopCond =
true;
5352 circuitState.addFragment(theModule,
"STOP_COND_FRAGMENT");
5354 circuitState.usedAssertVerboseCond =
true;
5355 circuitState.addFragment(theModule,
"ASSERT_VERBOSE_COND_FRAGMENT");
5357 addIfProceduralBlock(
5358 sv::MacroRefExprOp::create(builder, boolType,
5359 "ASSERT_VERBOSE_COND_"),
5360 [&]() { sv::ErrorOp::create(builder, message, messageOps); });
5361 addIfProceduralBlock(
5362 sv::MacroRefExprOp::create(builder, boolType,
"STOP_COND_"),
5363 [&]() { sv::FatalOp::create(builder); });
5369 case VerificationFlavor::SVA: {
5374 comb::createOrFoldNot(enable, builder,
true);
5376 builder.createOrFold<
comb::OrOp>(notEnable, predicate,
true);
5378 predicate = builder.createOrFold<
comb::AndOp>(enable, predicate,
true);
5382 sv::EventControl event;
5383 switch (opEventControl) {
5384 case EventControl::AtPosEdge:
5385 event = circt::sv::EventControl::AtPosEdge;
5387 case EventControl::AtEdge:
5388 event = circt::sv::EventControl::AtEdge;
5390 case EventControl::AtNegEdge:
5391 event = circt::sv::EventControl::AtNegEdge;
5397 circt::sv::EventControlAttr::get(builder.getContext(), event), clock,
5398 predicate, prefixedLabel, message, messageOps);
5401 case VerificationFlavor::None:
5403 "flavor `None` must be converted into one of concreate flavors");
5410 return emitGuards(op->getLoc(), guards,
emit);
5414LogicalResult FIRRTLLowering::visitStmt(AssertOp op) {
5415 return lowerVerificationStatement(
5416 op,
"assert__", op.getClock(), op.getPredicate(), op.getEnable(),
5417 op.getMessageAttr(), op.getSubstitutions(), op.getNameAttr(),
5418 op.getIsConcurrent(), op.getEventControl());
5422LogicalResult FIRRTLLowering::visitStmt(AssumeOp op) {
5423 return lowerVerificationStatement(
5424 op,
"assume__", op.getClock(), op.getPredicate(), op.getEnable(),
5425 op.getMessageAttr(), op.getSubstitutions(), op.getNameAttr(),
5426 op.getIsConcurrent(), op.getEventControl());
5430LogicalResult FIRRTLLowering::visitStmt(CoverOp op) {
5431 return lowerVerificationStatement(
5432 op,
"cover__", op.getClock(), op.getPredicate(), op.getEnable(),
5433 op.getMessageAttr(), op.getSubstitutions(), op.getNameAttr(),
5434 op.getIsConcurrent(), op.getEventControl());
5438LogicalResult FIRRTLLowering::visitStmt(UnclockedAssumeIntrinsicOp op) {
5443 auto guardsAttr = op->getAttrOfType<mlir::ArrayAttr>(
"guards");
5444 ArrayRef<Attribute> guards =
5445 guardsAttr ? guardsAttr.getValue() : ArrayRef<Attribute>();
5447 auto label = op.getNameAttr();
5448 StringAttr assumeLabel;
5449 if (label && !label.empty())
5451 StringAttr::get(builder.getContext(),
"assume__" + label.getValue());
5452 auto predicate = getLoweredValue(op.getPredicate());
5453 auto enable = getLoweredValue(op.getEnable());
5454 auto notEnable = comb::createOrFoldNot(enable, builder,
true);
5455 predicate = builder.createOrFold<
comb::OrOp>(notEnable, predicate,
true);
5457 SmallVector<Value> messageOps;
5458 for (
auto operand : op.getSubstitutions()) {
5459 auto loweredValue = getLoweredValue(operand);
5460 if (!loweredValue) {
5464 loweredValue = getOrCreateIntConstant(1, 0);
5466 messageOps.push_back(loweredValue);
5468 return emitGuards(op.getLoc(), guards, [&]() {
5469 sv::AlwaysOp::create(
5470 builder, ArrayRef(sv::EventControl::AtEdge), ArrayRef(predicate),
5472 if (op.getMessageAttr().getValue().empty())
5473 buildImmediateVerifOp(
5474 builder,
"assume", predicate,
5475 circt::sv::DeferAssertAttr::get(
5476 builder.getContext(), circt::sv::DeferAssert::Immediate),
5479 buildImmediateVerifOp(
5480 builder,
"assume", predicate,
5481 circt::sv::DeferAssertAttr::get(
5482 builder.getContext(), circt::sv::DeferAssert::Immediate),
5483 assumeLabel, op.getMessageAttr(), messageOps);
5488LogicalResult FIRRTLLowering::visitStmt(AttachOp op) {
5490 if (op.getAttached().size() < 2)
5493 SmallVector<Value, 4> inoutValues;
5494 for (
auto v : op.getAttached()) {
5495 inoutValues.push_back(getPossiblyInoutLoweredValue(v));
5496 if (!inoutValues.back()) {
5500 inoutValues.pop_back();
5504 if (!isa<hw::InOutType>(inoutValues.back().getType()))
5505 return op.emitError(
"operand isn't an inout type");
5508 if (inoutValues.size() < 2)
5519 bool isAttachInternalOnly =
5520 llvm::none_of(inoutValues, [](
auto v) {
return isa<BlockArgument>(v); });
5522 if (isAttachInternalOnly) {
5523 auto v0 = inoutValues.front();
5524 for (
auto v : inoutValues) {
5527 v.replaceAllUsesWith(v0);
5534 circuitState.addMacroDecl(builder.getStringAttr(
"SYNTHESIS"));
5535 circuitState.addMacroDecl(builder.getStringAttr(
"VERILATOR"));
5540 SmallVector<Value, 4> values;
5541 for (
auto inoutValue : inoutValues)
5542 values.push_back(getReadValue(inoutValue));
5544 for (
size_t i1 = 0, e = inoutValues.size(); i1 != e; ++i1) {
5545 for (
size_t i2 = 0; i2 != e; ++i2)
5553 sv::IfDefOp::create(
5554 builder,
"VERILATOR",
5556 sv::VerbatimOp::create(
5558 "`error \"Verilator does not support alias and thus "
5560 "arbitrarily connect bidirectional wires and ports\"");
5562 [&]() { sv::AliasOp::create(builder, inoutValues); });
5568LogicalResult FIRRTLLowering::visitStmt(BindOp op) {
5569 sv::BindOp::create(builder, op.getInstanceAttr());
5573LogicalResult FIRRTLLowering::fixupLTLOps() {
5574 if (ltlOpFixupWorklist.empty())
5576 LLVM_DEBUG(llvm::dbgs() <<
"Fixing up " << ltlOpFixupWorklist.size()
5580 for (
unsigned i = 0, e = ltlOpFixupWorklist.size(); i != e; ++i)
5581 for (
auto *user : ltlOpFixupWorklist[i]->getUsers())
5582 if (isa<
hw::WireOp>(user))
5583 ltlOpFixupWorklist.insert(user);
5586 while (!ltlOpFixupWorklist.empty()) {
5587 auto *op = ltlOpFixupWorklist.pop_back_val();
5590 if (
auto opIntf = dyn_cast_or_null<mlir::InferTypeOpInterface>(op)) {
5591 LLVM_DEBUG(llvm::dbgs() <<
"- Update " << *op <<
"\n");
5592 SmallVector<Type, 2> types;
5593 auto result = opIntf.inferReturnTypes(
5594 op->getContext(), op->getLoc(), op->getOperands(),
5595 op->getAttrDictionary(), op->getPropertiesStorage(), op->getRegions(),
5599 assert(types.size() == op->getNumResults());
5603 for (
auto [result, type] :
llvm::zip(op->getResults(), types)) {
5604 if (result.getType() == type)
5606 LLVM_DEBUG(llvm::dbgs()
5607 <<
" - Result #" << result.getResultNumber() <<
" from "
5608 << result.getType() <<
" to " << type <<
"\n");
5609 result.setType(type);
5610 for (
auto *user : result.getUsers())
5612 ltlOpFixupWorklist.insert(user);
5617 if (
auto wireOp = dyn_cast<hw::WireOp>(op)) {
5618 if (isa<ltl::SequenceType, ltl::PropertyType>(wireOp.getType())) {
5619 wireOp.replaceAllUsesWith(wireOp.getInput());
5620 LLVM_DEBUG(llvm::dbgs() <<
"- Remove " << wireOp <<
"\n");
5621 if (wireOp.use_empty())
5628 SmallPtrSet<Operation *, 4> usersReported;
5629 for (
auto *user : op->getUsers()) {
5630 if (!usersReported.insert(user).second)
5632 if (isa_and_nonnull<ltl::LTLDialect, verif::VerifDialect>(
5633 user->getDialect()))
5635 if (isa<hw::WireOp>(user))
5637 auto d = op->emitError(
5638 "verification operation used in a non-verification context");
5639 d.attachNote(user->getLoc())
5640 <<
"leaking outside verification context here";
assert(baseType &&"element must be base type")
static std::unique_ptr< Context > context
static LogicalResult emitFile(ArrayRef< Operation * > operations, StringRef filePath, raw_ostream &os)
Emits the given operation to a file represented by the passed ostream and file-path.
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, ValueRange operands, StringAttr &result)
static Value getSingleNonInstanceOperand(AttachOp op)
static IntType getWidestIntType(Type t1, Type t2)
Given two FIRRTL integer types, return the widest one.
static FailureOr< VectorizeOp > lowerBody(VectorizeOp op)
Vectorizes the body of the given arc.vectorize operation if it is not already vectorized.
static Location getLoc(DefSlot slot)
static Block * getBodyBlock(FModuleLike mod)
std::shared_ptr< calyx::CalyxLoweringState > loweringState
Instantiate one of these and use it to build typed backedges.
void abandon()
Abandon the backedges, suppressing any diagnostics if they are still active upon destruction of the b...
Backedge get(mlir::Type resultType, mlir::LocationAttr optionalLoc={})
Create a typed backedge.
mlir::LogicalResult clearOrEmitError()
Clear the backedges, erasing any remaining cursor ops.
Backedge is a wrapper class around a Value.
void setValue(mlir::Value)
A namespace that is used to store existing names and generate new names in some scope within the IR.
This class provides a read-only projection over the MLIR attributes that represent a set of annotatio...
bool removeAnnotations(llvm::function_ref< bool(Annotation)> predicate)
Remove all annotations from this annotation set for which predicate returns true.
bool removeAnnotation(Annotation anno)
Remove an annotation from this annotation set.
Annotation getAnnotation(StringRef className) const
If this annotation set has an annotation with the specified class name, return it.
This class provides a read-only projection of an annotation.
DictionaryAttr getDict() const
Get the data dictionary of this attribute.
AttrClass getMember(StringAttr name) const
Return a member of the annotation.
bool isClass(Args... names) const
Return true if this annotation matches any of the specified class names.
FIRRTLVisitor allows you to visit all of the expr/stmt/decls with one class declaration.
ResultType visitInvalidOp(Operation *op, ExtraArgs... args)
visitInvalidOp is an override point for non-FIRRTL dialect operations.
ResultType visitUnhandledOp(Operation *op, ExtraArgs... args)
visitUnhandledOp is an override point for FIRRTL dialect ops that the concrete visitor didn't bother ...
This graph tracks modules and where they are instantiated.
FModuleLike getTopLevelModule()
Get the module corresponding to the top-level module of a circuit.
This is the common base class between SIntType and UIntType.
This table tracks nlas and what modules participate in them.
This is an edge in the InstanceGraph.
create(str sym_name, Type type, str verilog_name=None)
create(data_type, name=None, sym_name=None)
Direction get(bool isOutput)
Returns an output direction if isOutput is true, otherwise returns an input direction.
StringRef getFragmentsAttrName()
Return the name of the fragments array attribute.
constexpr const char * extractCoverageAnnoClass
constexpr const char * elaborationArtefactsDirectoryAnnoClass
FIRRTLBaseType getBaseType(Type type)
If it is a base type, return it as is.
constexpr const char * extractGrandCentralClass
constexpr const char * blackBoxAnnoClass
constexpr const char * dontObfuscateModuleAnnoClass
constexpr const char * metadataDirectoryAttrName
constexpr const char * testBenchDirAnnoClass
std::pair< hw::InnerSymAttr, StringAttr > getOrAddInnerSym(MLIRContext *context, hw::InnerSymAttr attr, uint64_t fieldID, llvm::function_ref< hw::InnerSymbolNamespace &()> getNamespace)
Ensure that the the InnerSymAttr has a symbol on the field specified.
constexpr const char * dutAnnoClass
constexpr const char * forceNameAnnoClass
constexpr const char * noDedupAnnoClass
bool hasDroppableName(Operation *op)
Return true if the name is droppable.
constexpr const char * extractAssertAnnoClass
constexpr const char * verifBlackBoxAnnoClass
constexpr const char * blackBoxTargetDirAnnoClass
constexpr const char * verbatimBlackBoxAnnoClass
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)
The namespace of a CircuitOp, generally inhabited by modules.
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