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());
1342 loweringState.processRemainingAnnotations(oldModule, annos);
1347 FExtModuleOp oldModule, Block *topLevelModule,
1352 auto verbatimSource =
1353 getVerbatimSourceForExtModule(oldModule, topLevelModule,
loweringState);
1355 if (!verbatimSource)
1358 SmallVector<PortInfo> firrtlPorts = oldModule.getPorts();
1359 SmallVector<hw::PortInfo, 8> ports;
1360 if (failed(lowerPorts(firrtlPorts, ports, oldModule, oldModule.getName(),
1364 StringRef verilogName;
1365 if (
auto defName = oldModule.getDefname())
1366 verilogName = defName.value();
1368 auto builder = OpBuilder::atBlockEnd(topLevelModule);
1370 auto newModule = sv::SVVerbatimModuleOp::create(
1373 builder.getStringAttr(oldModule.getName()),
1375 FlatSymbolRefAttr::get(verbatimSource),
1376 parameters ? parameters : builder.getArrayAttr({}),
1377 verilogName.empty() ? StringAttr{}
1378 : builder.getStringAttr(verilogName));
1380 SymbolTable::setSymbolVisibility(newModule,
1381 SymbolTable::getSymbolVisibility(oldModule));
1383 bool hasOutputPort =
1384 llvm::any_of(firrtlPorts, [&](
auto p) {
return p.isOutput(); });
1385 if (!hasOutputPort &&
1388 newModule->setAttr(
"firrtl.extract.cover.extra", builder.getUnitAttr());
1393 loweringState.processRemainingAnnotations(oldModule, annos);
1398FIRRTLModuleLowering::lowerMemModule(FMemModuleOp oldModule,
1399 Block *topLevelModule,
1402 SmallVector<PortInfo> firrtlPorts = oldModule.getPorts();
1403 SmallVector<hw::PortInfo, 8> ports;
1404 if (failed(lowerPorts(firrtlPorts, ports, oldModule, oldModule.getName(),
1409 auto builder = OpBuilder::atBlockEnd(topLevelModule);
1410 auto newModule = hw::HWModuleExternOp::create(
1411 builder, oldModule.getLoc(), oldModule.getModuleNameAttr(), ports,
1412 oldModule.getModuleNameAttr());
1420FIRRTLModuleLowering::lowerModule(FModuleOp oldModule, Block *topLevelModule,
1423 SmallVector<PortInfo> firrtlPorts = oldModule.getPorts();
1424 SmallVector<hw::PortInfo, 8> ports;
1425 if (failed(lowerPorts(firrtlPorts, ports, oldModule, oldModule.getName(),
1430 auto builder = OpBuilder::atBlockEnd(topLevelModule);
1431 auto nameAttr = builder.getStringAttr(oldModule.getName());
1433 hw::HWModuleOp::create(builder, oldModule.getLoc(), nameAttr, ports);
1435 if (
auto comment = oldModule->getAttrOfType<StringAttr>(
"comment"))
1436 newModule.setCommentAttr(comment);
1439 SmallVector<StringRef, 13> attrNames = {
1440 "annotations",
"convention",
"layers",
1441 "portNames",
"sym_name",
"portDirections",
1442 "portTypes",
"portAnnotations",
"portSymbols",
1443 "portLocations",
"parameters", SymbolTable::getVisibilityAttrName(),
1446 DenseSet<StringRef> attrSet(attrNames.begin(), attrNames.end());
1447 SmallVector<NamedAttribute> newAttrs(newModule->getAttrs());
1449 llvm::make_filter_range(oldModule->getAttrs(), [&](auto namedAttr) {
1450 return !attrSet.count(namedAttr.getName()) &&
1451 !newModule->getAttrDictionary().contains(namedAttr.getName());
1453 newAttrs.push_back(i);
1455 newModule->setAttrs(newAttrs);
1459 SymbolTable::setSymbolVisibility(newModule,
1460 SymbolTable::getSymbolVisibility(oldModule));
1466 newModule->setAttr(
"firrtl.extract.cover.extra", builder.getUnitAttr());
1470 if (
auto testBenchDir =
loweringState.getTestBenchDirectory())
1472 if (!newModule->hasAttr(
"output_file"))
1473 newModule->setAttr(
"output_file", testBenchDir);
1474 newModule->setAttr(
"firrtl.extract.do_not_extract",
1475 builder.getUnitAttr());
1476 newModule.setCommentAttr(
1477 builder.getStringAttr(
"VCS coverage exclude_file"));
1483 loweringState.processRemainingAnnotations(oldModule, annos);
1492 Operation *insertPoint) {
1493 if (!value.hasOneUse())
1496 auto attach = dyn_cast<AttachOp>(*value.user_begin());
1497 if (!attach || attach.getNumOperands() != 2)
1501 auto loweredType =
lowerType(value.getType());
1502 if (loweredType.isInteger(0))
1507 auto attachedValue = attach.getOperand(attach.getOperand(0) == value);
1508 auto *op = attachedValue.getDefiningOp();
1509 if (op && op->getBlock() == insertPoint->getBlock() &&
1510 !op->isBeforeInBlock(insertPoint))
1515 ImplicitLocOpBuilder builder(insertPoint->getLoc(), insertPoint);
1531 if (type_isa<AnalogType>(flipValue.getType()))
1534 Operation *connectOp =
nullptr;
1535 for (
auto &use : flipValue.getUses()) {
1538 if (use.getOperandNumber() != 0)
1540 if (!isa<ConnectOp, MatchingConnectOp>(use.getOwner()))
1546 connectOp = use.getOwner();
1556 loweringState.lowerType(flipValue.getType(), flipValue.getLoc());
1557 if (loweredType.isInteger(0))
1562 ImplicitLocOpBuilder builder(insertPoint->getLoc(), insertPoint);
1564 auto connectSrc = connectOp->getOperand(1);
1567 if (!isa<FIRRTLType>(connectSrc.getType())) {
1573 if (!type_cast<FIRRTLBaseType>(connectSrc.getType()).isPassive())
1575 mlir::UnrealizedConversionCastOp::create(
1577 type_cast<FIRRTLBaseType>(connectSrc.getType()).getPassiveType(),
1583 auto destTy = type_cast<FIRRTLBaseType>(flipValue.getType()).getPassiveType();
1585 if (destTy != connectSrc.getType() &&
1586 (isa<BaseTypeAliasType>(connectSrc.getType()) ||
1587 isa<BaseTypeAliasType>(destTy))) {
1589 builder.createOrFold<BitCastOp>(flipValue.getType(), connectSrc);
1591 if (!destTy.isGround()) {
1593 if (destTy != type_cast<FIRRTLType>(connectSrc.getType()))
1595 }
else if (destTy.getBitWidthOrSentinel() !=
1596 type_cast<FIRRTLBaseType>(connectSrc.getType())
1597 .getBitWidthOrSentinel()) {
1600 auto destWidth = destTy.getBitWidthOrSentinel();
1601 assert(destWidth != -1 &&
"must know integer widths");
1602 connectSrc = builder.createOrFold<PadPrimOp>(destTy, connectSrc, destWidth);
1614 SmallVector<SubfieldOp> accesses;
1615 for (
auto *op : structValue.getUsers()) {
1616 assert(isa<SubfieldOp>(op));
1617 auto fieldAccess = cast<SubfieldOp>(op);
1619 fieldAccess.getInput().getType().base().getElementIndex(field);
1620 if (elemIndex && *elemIndex == fieldAccess.getFieldIndex())
1621 accesses.push_back(fieldAccess);
1629LogicalResult FIRRTLModuleLowering::lowerModulePortsAndMoveBody(
1632 ImplicitLocOpBuilder bodyBuilder(oldModule.getLoc(), newModule.getBody());
1639 bodyBuilder.setInsertionPoint(cursor);
1642 SmallVector<PortInfo> firrtlPorts = oldModule.getPorts();
1643 assert(oldModule.getBody().getNumArguments() == firrtlPorts.size() &&
1644 "port count mismatch");
1646 SmallVector<Value, 4> outputs;
1649 auto *outputOp = newModule.getBodyBlock()->getTerminator();
1650 ImplicitLocOpBuilder outputBuilder(oldModule.getLoc(), outputOp);
1652 unsigned nextHWInputArg = 0;
1653 int hwPortIndex = -1;
1654 for (
auto [firrtlPortIndex, port] :
llvm::enumerate(firrtlPorts)) {
1656 auto oldArg = oldModule.getBody().getArgument(firrtlPortIndex);
1659 type_isa<FIRRTLBaseType>(port.type) &&
1660 type_cast<FIRRTLBaseType>(port.type).getBitWidthOrSentinel() == 0;
1664 if (!port.isOutput() && !isZeroWidth) {
1667 Value newArg = newModule.getBody().getArgument(nextHWInputArg++);
1673 oldArg.replaceAllUsesWith(newArg);
1679 if (isZeroWidth && port.isInput()) {
1681 WireOp::create(bodyBuilder, port.type,
1682 "." + port.getName().str() +
".0width_input")
1684 oldArg.replaceAllUsesWith(newArg);
1692 outputs.push_back(value);
1693 assert(oldArg.use_empty() &&
"should have removed all uses of oldArg");
1699 auto newArg = WireOp::create(bodyBuilder, port.type,
1700 "." + port.getName().str() +
".output");
1703 oldArg.replaceAllUsesWith(newArg.getResult());
1706 auto resultHWType =
loweringState.lowerType(port.type, port.loc);
1707 if (!resultHWType.isInteger(0)) {
1710 outputs.push_back(output);
1713 if (
auto sym = newModule.getPort(hwPortIndex).getSym()) {
1714 newArg.setInnerSymAttr(sym);
1715 newModule.setPortSymbolAttr(hwPortIndex, {});
1721 outputOp->setOperands(outputs);
1724 auto &oldBlockInstList = oldModule.getBodyBlock()->getOperations();
1725 auto &newBlockInstList = newModule.getBodyBlock()->getOperations();
1726 newBlockInstList.splice(Block::iterator(cursor), oldBlockInstList,
1727 oldBlockInstList.begin(), oldBlockInstList.end());
1738FIRRTLModuleLowering::lowerFormalBody(verif::FormalOp newOp,
1740 auto builder = OpBuilder::atBlockEnd(&newOp.getBody().front());
1745 auto oldOp = cast<FormalOp>(
loweringState.getOldModule(newOp));
1746 auto moduleName = oldOp.getModuleNameAttr().getAttr();
1747 auto oldModule = cast<FModuleOp>(
1748 loweringState.getInstanceGraph().lookup(moduleName)->getModule());
1749 auto newModule = cast<hw::HWModuleOp>(
loweringState.getNewModule(oldModule));
1752 SmallVector<Value> symbolicInputs;
1753 for (
auto arg : newModule.getBody().getArguments())
1754 symbolicInputs.push_back(
1755 verif::SymbolicValueOp::create(builder, arg.
getLoc(), arg.getType()));
1758 hw::InstanceOp::create(builder, newOp.getLoc(), newModule,
1759 newModule.getNameAttr(), symbolicInputs);
1766FIRRTLModuleLowering::lowerSimulationBody(verif::SimulationOp newOp,
1768 auto builder = OpBuilder::atBlockEnd(newOp.getBody());
1771 auto oldOp = cast<SimulationOp>(
loweringState.getOldModule(newOp));
1772 auto moduleName = oldOp.getModuleNameAttr().getAttr();
1773 auto oldModule = cast<FModuleLike>(
1774 *
loweringState.getInstanceGraph().lookup(moduleName)->getModule());
1776 cast<hw::HWModuleLike>(
loweringState.getNewModule(oldModule));
1780 SmallVector<Value> inputs(newOp.getBody()->args_begin(),
1781 newOp.getBody()->args_end());
1782 auto instOp = hw::InstanceOp::create(builder, newOp.getLoc(), newModule,
1783 newModule.getNameAttr(), inputs);
1784 verif::YieldOp::create(builder, newOp.getLoc(), instOp.getResults());
1794struct FIRRTLLowering :
public FIRRTLVisitor<FIRRTLLowering, LogicalResult> {
1796 FIRRTLLowering(
hw::HWModuleOp module, CircuitLoweringState &circuitState)
1797 : theModule(module), circuitState(circuitState),
1798 builder(module.
getLoc(), module.getContext()), moduleNamespace(module),
1799 backedgeBuilder(builder, module.
getLoc()) {}
1801 LogicalResult
run();
1804 Value getOrCreateClockConstant(seq::ClockConst clock);
1805 Value getOrCreateIntConstant(
const APInt &value);
1806 Value getOrCreateIntConstant(
unsigned numBits, uint64_t val,
1807 bool isSigned =
false) {
1808 return getOrCreateIntConstant(APInt(numBits, val, isSigned));
1810 Attribute getOrCreateAggregateConstantAttribute(Attribute value, Type type);
1811 Value getOrCreateXConstant(
unsigned numBits);
1812 Value getOrCreateZConstant(Type type);
1813 Value getPossiblyInoutLoweredValue(Value value);
1814 Value getLoweredValue(Value value);
1815 Value getLoweredNonClockValue(Value value);
1816 Value getLoweredAndExtendedValue(Value value, Type destType);
1817 Value getLoweredAndExtOrTruncValue(Value value, Type destType);
1818 LogicalResult setLowering(Value orig, Value result);
1819 LogicalResult setPossiblyFoldedLowering(Value orig, Value result);
1820 template <
typename ResultOpType,
typename... CtorArgTypes>
1821 LogicalResult setLoweringTo(Operation *orig, CtorArgTypes... args);
1822 template <
typename ResultOpType,
typename... CtorArgTypes>
1823 LogicalResult setLoweringToLTL(Operation *orig, CtorArgTypes... args);
1824 Backedge createBackedge(Location loc, Type type);
1825 Backedge createBackedge(Value orig, Type type);
1826 bool updateIfBackedge(Value dest, Value src);
1829 bool requiresInnerSymbol(hw::InnerSymbolOpInterface op) {
1834 if (
auto forceable = dyn_cast<Forceable>(op.getOperation()))
1835 if (forceable.isForceable())
1843 hw::InnerSymAttr lowerInnerSymbol(hw::InnerSymbolOpInterface op) {
1844 auto attr = op.getInnerSymAttr();
1848 if (requiresInnerSymbol(op))
1850 op.getContext(), attr, 0,
1855 void runWithInsertionPointAtEndOfBlock(
const std::function<
void(
void)> &fn,
1859 Value getReadValue(Value v);
1861 Value getNonClockValue(Value v);
1863 void addToAlwaysBlock(sv::EventControl clockEdge, Value clock,
1864 sv::ResetType resetStyle, sv::EventControl resetEdge,
1865 Value reset,
const std::function<
void(
void)> &body = {},
1866 const std::function<void(
void)> &resetBody = {});
1867 void addToAlwaysBlock(Value clock,
1868 const std::function<
void(
void)> &body = {}) {
1869 addToAlwaysBlock(sv::EventControl::AtPosEdge, clock, sv::ResetType(),
1870 sv::EventControl(), Value(), body,
1871 std::function<
void(
void)>());
1874 LogicalResult emitGuards(Location loc, ArrayRef<Attribute> guards,
1875 std::function<
void(
void)>
emit);
1876 void addToIfDefBlock(StringRef cond, std::function<
void(
void)> thenCtor,
1877 std::function<
void(
void)> elseCtor = {});
1878 void addToInitialBlock(std::function<
void(
void)> body);
1879 void addIfProceduralBlock(Value cond, std::function<
void(
void)> thenCtor,
1880 std::function<
void(
void)> elseCtor = {});
1881 Value getExtOrTruncAggregateValue(Value array,
FIRRTLBaseType sourceType,
1883 bool allowTruncate);
1884 Value createArrayIndexing(Value array, Value index);
1885 Value createValueWithMuxAnnotation(Operation *op,
bool isMux2);
1887 using FIRRTLVisitor<FIRRTLLowering, LogicalResult>::visitExpr;
1888 using FIRRTLVisitor<FIRRTLLowering, LogicalResult>::visitDecl;
1889 using FIRRTLVisitor<FIRRTLLowering, LogicalResult>::visitStmt;
1892 enum UnloweredOpResult { AlreadyLowered, NowLowered, LoweringFailure };
1893 UnloweredOpResult handleUnloweredOp(Operation *op);
1894 LogicalResult visitExpr(ConstantOp op);
1895 LogicalResult visitExpr(SpecialConstantOp op);
1896 LogicalResult visitExpr(SubindexOp op);
1897 LogicalResult visitExpr(SubaccessOp op);
1898 LogicalResult visitExpr(SubfieldOp op);
1899 LogicalResult visitExpr(VectorCreateOp op);
1900 LogicalResult visitExpr(BundleCreateOp op);
1901 LogicalResult visitExpr(FEnumCreateOp op);
1902 LogicalResult visitExpr(AggregateConstantOp op);
1903 LogicalResult visitExpr(IsTagOp op);
1904 LogicalResult visitExpr(SubtagOp op);
1905 LogicalResult visitExpr(TagExtractOp op);
1908 if (
auto castOp = dyn_cast<mlir::UnrealizedConversionCastOp>(op))
1909 return visitUnrealizedConversionCast(castOp);
1914 LogicalResult visitDecl(WireOp op);
1915 LogicalResult visitDecl(NodeOp op);
1916 LogicalResult visitDecl(RegOp op);
1917 LogicalResult visitDecl(RegResetOp op);
1918 LogicalResult visitDecl(MemOp op);
1919 LogicalResult visitDecl(InstanceOp oldInstance);
1920 LogicalResult visitDecl(VerbatimWireOp op);
1921 LogicalResult visitDecl(ContractOp op);
1924 LogicalResult lowerNoopCast(Operation *op);
1925 LogicalResult visitExpr(AsSIntPrimOp op);
1926 LogicalResult visitExpr(AsUIntPrimOp op);
1927 LogicalResult visitExpr(AsClockPrimOp op);
1928 LogicalResult visitExpr(AsAsyncResetPrimOp op) {
return lowerNoopCast(op); }
1930 LogicalResult visitExpr(HWStructCastOp op);
1931 LogicalResult visitExpr(BitCastOp op);
1933 visitUnrealizedConversionCast(mlir::UnrealizedConversionCastOp op);
1934 LogicalResult visitExpr(CvtPrimOp op);
1935 LogicalResult visitExpr(NotPrimOp op);
1936 LogicalResult visitExpr(NegPrimOp op);
1937 LogicalResult visitExpr(PadPrimOp op);
1938 LogicalResult visitExpr(XorRPrimOp op);
1939 LogicalResult visitExpr(AndRPrimOp op);
1940 LogicalResult visitExpr(OrRPrimOp op);
1943 template <
typename ResultUnsignedOpType,
1944 typename ResultSignedOpType = ResultUnsignedOpType>
1945 LogicalResult lowerBinOp(Operation *op);
1946 template <
typename ResultOpType>
1947 LogicalResult lowerBinOpToVariadic(Operation *op);
1949 template <
typename ResultOpType>
1950 LogicalResult lowerElementwiseLogicalOp(Operation *op);
1952 LogicalResult lowerCmpOp(Operation *op, ICmpPredicate signedOp,
1953 ICmpPredicate unsignedOp);
1954 template <
typename SignedOp,
typename Un
signedOp>
1955 LogicalResult lowerDivLikeOp(Operation *op);
1957 LogicalResult visitExpr(CatPrimOp op);
1959 LogicalResult visitExpr(AndPrimOp op) {
1960 return lowerBinOpToVariadic<comb::AndOp>(op);
1962 LogicalResult visitExpr(OrPrimOp op) {
1963 return lowerBinOpToVariadic<comb::OrOp>(op);
1965 LogicalResult visitExpr(XorPrimOp op) {
1966 return lowerBinOpToVariadic<comb::XorOp>(op);
1968 LogicalResult visitExpr(ElementwiseOrPrimOp op) {
1969 return lowerElementwiseLogicalOp<comb::OrOp>(op);
1971 LogicalResult visitExpr(ElementwiseAndPrimOp op) {
1972 return lowerElementwiseLogicalOp<comb::AndOp>(op);
1974 LogicalResult visitExpr(ElementwiseXorPrimOp op) {
1975 return lowerElementwiseLogicalOp<comb::XorOp>(op);
1977 LogicalResult visitExpr(AddPrimOp op) {
1978 return lowerBinOpToVariadic<comb::AddOp>(op);
1980 LogicalResult visitExpr(EQPrimOp op) {
1981 return lowerCmpOp(op, ICmpPredicate::eq, ICmpPredicate::eq);
1983 LogicalResult visitExpr(NEQPrimOp op) {
1984 return lowerCmpOp(op, ICmpPredicate::ne, ICmpPredicate::ne);
1986 LogicalResult visitExpr(LTPrimOp op) {
1987 return lowerCmpOp(op, ICmpPredicate::slt, ICmpPredicate::ult);
1989 LogicalResult visitExpr(LEQPrimOp op) {
1990 return lowerCmpOp(op, ICmpPredicate::sle, ICmpPredicate::ule);
1992 LogicalResult visitExpr(GTPrimOp op) {
1993 return lowerCmpOp(op, ICmpPredicate::sgt, ICmpPredicate::ugt);
1995 LogicalResult visitExpr(GEQPrimOp op) {
1996 return lowerCmpOp(op, ICmpPredicate::sge, ICmpPredicate::uge);
1999 LogicalResult visitExpr(SubPrimOp op) {
return lowerBinOp<comb::SubOp>(op); }
2000 LogicalResult visitExpr(MulPrimOp op) {
2001 return lowerBinOpToVariadic<comb::MulOp>(op);
2003 LogicalResult visitExpr(DivPrimOp op) {
2004 return lowerDivLikeOp<comb::DivSOp, comb::DivUOp>(op);
2006 LogicalResult visitExpr(RemPrimOp op) {
2007 return lowerDivLikeOp<comb::ModSOp, comb::ModUOp>(op);
2011 LogicalResult visitExpr(IsXIntrinsicOp op);
2012 LogicalResult visitExpr(PlusArgsTestIntrinsicOp op);
2013 LogicalResult visitExpr(PlusArgsValueIntrinsicOp op);
2014 LogicalResult visitStmt(FPGAProbeIntrinsicOp op);
2015 LogicalResult visitExpr(ClockInverterIntrinsicOp op);
2016 LogicalResult visitExpr(ClockDividerIntrinsicOp op);
2017 LogicalResult visitExpr(SizeOfIntrinsicOp op);
2018 LogicalResult visitExpr(ClockGateIntrinsicOp op);
2019 LogicalResult visitExpr(LTLAndIntrinsicOp op);
2020 LogicalResult visitExpr(LTLOrIntrinsicOp op);
2021 LogicalResult visitExpr(LTLIntersectIntrinsicOp op);
2022 LogicalResult visitExpr(LTLDelayIntrinsicOp op);
2023 LogicalResult visitExpr(LTLConcatIntrinsicOp op);
2024 LogicalResult visitExpr(LTLRepeatIntrinsicOp op);
2025 LogicalResult visitExpr(LTLGoToRepeatIntrinsicOp op);
2026 LogicalResult visitExpr(LTLNonConsecutiveRepeatIntrinsicOp op);
2027 LogicalResult visitExpr(LTLNotIntrinsicOp op);
2028 LogicalResult visitExpr(LTLImplicationIntrinsicOp op);
2029 LogicalResult visitExpr(LTLUntilIntrinsicOp op);
2030 LogicalResult visitExpr(LTLEventuallyIntrinsicOp op);
2031 LogicalResult visitExpr(LTLClockIntrinsicOp op);
2033 template <
typename TargetOp,
typename IntrinsicOp>
2034 LogicalResult lowerVerifIntrinsicOp(IntrinsicOp op);
2035 LogicalResult visitStmt(VerifAssertIntrinsicOp op);
2036 LogicalResult visitStmt(VerifAssumeIntrinsicOp op);
2037 LogicalResult visitStmt(VerifCoverIntrinsicOp op);
2038 LogicalResult visitStmt(VerifRequireIntrinsicOp op);
2039 LogicalResult visitStmt(VerifEnsureIntrinsicOp op);
2040 LogicalResult visitExpr(HasBeenResetIntrinsicOp op);
2041 LogicalResult visitStmt(UnclockedAssumeIntrinsicOp op);
2044 LogicalResult visitExpr(BitsPrimOp op);
2045 LogicalResult visitExpr(InvalidValueOp op);
2046 LogicalResult visitExpr(HeadPrimOp op);
2047 LogicalResult visitExpr(ShlPrimOp op);
2048 LogicalResult visitExpr(ShrPrimOp op);
2049 LogicalResult visitExpr(DShlPrimOp op) {
2050 return lowerDivLikeOp<comb::ShlOp, comb::ShlOp>(op);
2052 LogicalResult visitExpr(DShrPrimOp op) {
2053 return lowerDivLikeOp<comb::ShrSOp, comb::ShrUOp>(op);
2055 LogicalResult visitExpr(DShlwPrimOp op) {
2056 return lowerDivLikeOp<comb::ShlOp, comb::ShlOp>(op);
2058 LogicalResult visitExpr(TailPrimOp op);
2059 LogicalResult visitExpr(MuxPrimOp op);
2060 LogicalResult visitExpr(Mux2CellIntrinsicOp op);
2061 LogicalResult visitExpr(Mux4CellIntrinsicOp op);
2062 LogicalResult visitExpr(MultibitMuxOp op);
2063 LogicalResult visitExpr(VerbatimExprOp op);
2064 LogicalResult visitExpr(XMRRefOp op);
2065 LogicalResult visitExpr(XMRDerefOp op);
2068 LogicalResult visitExpr(TimeOp op);
2069 LogicalResult visitExpr(HierarchicalModuleNameOp op);
2072 LogicalResult lowerVerificationStatement(
2073 Operation *op, StringRef labelPrefix, Value clock, Value predicate,
2074 Value enable, StringAttr messageAttr, ValueRange operands,
2075 StringAttr nameAttr,
bool isConcurrent, EventControl eventControl);
2077 LogicalResult visitStmt(SkipOp op);
2079 FailureOr<bool> lowerConnect(Value dest, Value srcVal);
2080 LogicalResult visitStmt(ConnectOp op);
2081 LogicalResult visitStmt(MatchingConnectOp op);
2082 LogicalResult visitStmt(ForceOp op);
2084 std::optional<Value> getLoweredFmtOperand(Value operand);
2085 LogicalResult loweredFmtOperands(ValueRange operands,
2086 SmallVectorImpl<Value> &loweredOperands);
2087 FailureOr<Value> callFileDescriptorLib(
const FileDescriptorInfo &info);
2091 LogicalResult lowerStatementWithFd(
2092 const FileDescriptorInfo &fileDescriptorInfo, Value clock, Value cond,
2093 const std::function<LogicalResult(Value)> &fn,
bool usePrintfCond);
2097 LogicalResult visitPrintfLike(T op,
2098 const FileDescriptorInfo &fileDescriptorInfo,
2099 bool usePrintfCond);
2100 LogicalResult visitStmt(PrintFOp op) {
return visitPrintfLike(op, {},
true); }
2101 LogicalResult visitStmt(FPrintFOp op);
2102 LogicalResult visitStmt(FFlushOp op);
2103 LogicalResult visitStmt(StopOp op);
2104 LogicalResult visitStmt(AssertOp op);
2105 LogicalResult visitStmt(AssumeOp op);
2106 LogicalResult visitStmt(CoverOp op);
2107 LogicalResult visitStmt(AttachOp op);
2108 LogicalResult visitStmt(RefForceOp op);
2109 LogicalResult visitStmt(RefForceInitialOp op);
2110 LogicalResult visitStmt(RefReleaseOp op);
2111 LogicalResult visitStmt(RefReleaseInitialOp op);
2112 LogicalResult visitStmt(BindOp op);
2114 FailureOr<Value> lowerSubindex(SubindexOp op, Value input);
2115 FailureOr<Value> lowerSubaccess(SubaccessOp op, Value input);
2116 FailureOr<Value> lowerSubfield(SubfieldOp op, Value input);
2118 LogicalResult fixupLTLOps();
2121 return circuitState.lowerType(type, builder.getLoc());
2129 CircuitLoweringState &circuitState;
2132 ImplicitLocOpBuilder builder;
2137 DenseMap<Value, Value> valueMapping;
2141 DenseMap<Value, Value> fromClockMapping;
2145 DenseMap<Attribute, Value> hwConstantMap;
2146 DenseMap<std::pair<Attribute, Type>, Attribute> hwAggregateConstantMap;
2150 DenseMap<unsigned, Value> hwConstantXMap;
2151 DenseMap<Type, Value> hwConstantZMap;
2157 DenseMap<Value, Value> readInOutCreated;
2160 DenseMap<StringAttr, sv::RegOp> fileNameToFileDescriptor;
2164 using AlwaysKeyType = std::tuple<
Block *, sv::EventControl, Value,
2165 sv::ResetType, sv::EventControl, Value>;
2182 llvm::MapVector<Value, Value> backedges;
2189 DenseSet<Operation *> maybeUnusedValues;
2191 void maybeUnused(Operation *op) { maybeUnusedValues.insert(op); }
2192 void maybeUnused(Value value) {
2193 if (
auto *op = value.getDefiningOp())
2205 SetVector<Operation *> ltlOpFixupWorklist;
2210 SmallVector<std::pair<Block::iterator, Block::iterator>> worklist;
2212 void addToWorklist(Block &block) {
2213 worklist.push_back({block.begin(), block.end()});
2215 void addToWorklist(Region ®ion) {
2216 for (
auto &block :
llvm::reverse(region))
2217 addToWorklist(block);
2228LogicalResult FIRRTLModuleLowering::lowerFileBody(emit::FileOp fileOp) {
2229 OpBuilder b(&getContext());
2230 fileOp->walk([&](Operation *op) {
2231 if (
auto bindOp = dyn_cast<BindOp>(op)) {
2232 b.setInsertionPointAfter(bindOp);
2233 sv::BindOp::create(b, bindOp.getLoc(), bindOp.getInstanceAttr());
2241FIRRTLModuleLowering::lowerBody(Operation *op,
2243 if (
auto moduleOp = dyn_cast<hw::HWModuleOp>(op))
2245 if (
auto formalOp = dyn_cast<verif::FormalOp>(op))
2247 if (
auto simulationOp = dyn_cast<verif::SimulationOp>(op))
2249 if (
auto fileOp = dyn_cast<emit::FileOp>(op))
2250 return lowerFileBody(fileOp);
2255LogicalResult FIRRTLLowering::run() {
2258 for (
auto arg : theModule.
getBodyBlock()->getArguments())
2259 if (failed(setLowering(arg, arg)))
2266 addToWorklist(theModule.getBody());
2267 SmallVector<Operation *, 16> opsToRemove;
2269 while (!worklist.empty()) {
2270 auto &[opsIt, opsEnd] = worklist.back();
2271 if (opsIt == opsEnd) {
2272 worklist.pop_back();
2275 Operation *op = &*opsIt++;
2277 builder.setInsertionPoint(op);
2278 builder.setLoc(op->getLoc());
2279 auto done = succeeded(dispatchVisitor(op));
2280 circuitState.processRemainingAnnotations(op,
AnnotationSet(op));
2282 opsToRemove.push_back(op);
2284 switch (handleUnloweredOp(op)) {
2285 case AlreadyLowered:
2288 opsToRemove.push_back(op);
2290 case LoweringFailure:
2302 for (
auto &[backedge, value] : backedges) {
2303 SmallVector<Location> driverLocs;
2309 if (backedge == value) {
2310 Location edgeLoc = backedge.getLoc();
2311 if (driverLocs.empty()) {
2312 mlir::emitError(edgeLoc,
"sink does not have a driver");
2314 auto diag = mlir::emitError(edgeLoc,
"sink in combinational loop");
2315 for (
auto loc : driverLocs)
2316 diag.attachNote(loc) <<
"through driver here";
2322 auto *it = backedges.find(value);
2323 if (it == backedges.end())
2326 driverLocs.push_back(value.getLoc());
2329 if (
auto *defOp = backedge.getDefiningOp())
2330 maybeUnusedValues.erase(defOp);
2331 backedge.replaceAllUsesWith(value);
2339 while (!opsToRemove.empty()) {
2340 auto *op = opsToRemove.pop_back_val();
2345 for (
auto result : op->getResults()) {
2349 auto builder = OpBuilder::atBlockBegin(theModule.getBodyBlock());
2351 builder.getIntegerType(0), 0);
2352 maybeUnusedValues.insert(zeroI0);
2354 result.replaceAllUsesWith(zeroI0);
2357 if (!op->use_empty()) {
2358 auto d = op->emitOpError(
2359 "still has uses; should remove ops in reverse order of visitation");
2360 SmallPtrSet<Operation *, 2> visited;
2361 for (
auto *user : op->getUsers())
2362 if (visited.insert(user).second)
2363 d.attachNote(user->
getLoc())
2364 <<
"used by " << user->
getName() <<
" op";
2367 maybeUnusedValues.erase(op);
2372 while (!maybeUnusedValues.empty()) {
2373 auto it = maybeUnusedValues.begin();
2375 maybeUnusedValues.erase(it);
2376 if (!isOpTriviallyDead(op))
2378 for (
auto operand : op->getOperands())
2379 if (auto *defOp = operand.getDefiningOp())
2380 maybeUnusedValues.insert(defOp);
2386 if (failed(fixupLTLOps()))
2397Value FIRRTLLowering::getOrCreateClockConstant(seq::ClockConst clock) {
2398 auto attr = seq::ClockConstAttr::get(theModule.getContext(), clock);
2400 auto &entry = hwConstantMap[attr];
2404 OpBuilder entryBuilder(&theModule.getBodyBlock()->front());
2405 entry = seq::ConstClockOp::create(entryBuilder, builder.getLoc(), attr);
2411Value FIRRTLLowering::getOrCreateIntConstant(
const APInt &value) {
2412 auto attr = builder.getIntegerAttr(
2413 builder.getIntegerType(value.getBitWidth()), value);
2415 auto &entry = hwConstantMap[attr];
2419 OpBuilder entryBuilder(&theModule.getBodyBlock()->front());
2426Attribute FIRRTLLowering::getOrCreateAggregateConstantAttribute(Attribute value,
2429 if (hw::type_isa<IntegerType>(type))
2430 return builder.getIntegerAttr(type, cast<IntegerAttr>(value).getValue());
2432 auto cache = hwAggregateConstantMap.lookup({value, type});
2437 SmallVector<Attribute> values;
2438 for (
auto e :
llvm::enumerate(cast<ArrayAttr>(value))) {
2440 if (
auto array = hw::type_dyn_cast<hw::ArrayType>(type))
2441 subType = array.getElementType();
2442 else if (
auto structType = hw::type_dyn_cast<hw::StructType>(type))
2443 subType = structType.getElements()[e.index()].type;
2445 assert(
false &&
"type must be either array or struct");
2447 values.push_back(getOrCreateAggregateConstantAttribute(e.value(), subType));
2451 if (hw::type_isa<hw::ArrayType>(type))
2452 std::reverse(values.begin(), values.end());
2454 auto &entry = hwAggregateConstantMap[{value, type}];
2455 entry = builder.getArrayAttr(values);
2463 const std::function<LogicalResult()> &fn) {
2464 assert(failedOperand &&
"Should be called on the failed operand");
2472Value FIRRTLLowering::getOrCreateXConstant(
unsigned numBits) {
2474 auto &entry = hwConstantXMap[numBits];
2478 OpBuilder entryBuilder(&theModule.getBodyBlock()->front());
2479 entry = sv::ConstantXOp::create(entryBuilder, builder.getLoc(),
2480 entryBuilder.getIntegerType(numBits));
2484Value FIRRTLLowering::getOrCreateZConstant(Type type) {
2485 auto &entry = hwConstantZMap[type];
2487 OpBuilder entryBuilder(&theModule.getBodyBlock()->front());
2488 entry = sv::ConstantZOp::create(entryBuilder, builder.getLoc(), type);
2497Value FIRRTLLowering::getPossiblyInoutLoweredValue(Value value) {
2499 if (
auto lowering = valueMapping.lookup(value)) {
2500 assert(!isa<FIRRTLType>(lowering.getType()) &&
2501 "Lowered value should be a non-FIRRTL value");
2510Value FIRRTLLowering::getLoweredValue(Value value) {
2511 auto result = getPossiblyInoutLoweredValue(value);
2517 if (isa<hw::InOutType>(result.getType()))
2518 return getReadValue(result);
2524Value FIRRTLLowering::getLoweredNonClockValue(Value value) {
2525 auto result = getLoweredValue(value);
2529 if (hw::type_isa<seq::ClockType>(result.getType()))
2530 return getNonClockValue(result);
2538Value FIRRTLLowering::getExtOrTruncAggregateValue(Value array,
2541 bool allowTruncate) {
2542 SmallVector<Value> resultBuffer;
2547 auto srcWidth = firrtl::type_cast<IntType>(sourceType).getWidthOrSentinel();
2548 auto destWidth = firrtl::type_cast<IntType>(destType).getWidthOrSentinel();
2549 auto resultType = builder.getIntegerType(destWidth);
2551 if (srcWidth == destWidth)
2554 if (srcWidth > destWidth) {
2558 builder.emitError(
"operand should not be a truncation");
2562 if (firrtl::type_cast<IntType>(sourceType).isSigned())
2563 return comb::createOrFoldSExt(value, resultType, builder);
2564 auto zero = getOrCreateIntConstant(destWidth - srcWidth, 0);
2572 return TypeSwitch<FIRRTLBaseType, LogicalResult>(srcType)
2573 .Case<FVectorType>([&](
auto srcVectorType) {
2574 auto destVectorType = firrtl::type_cast<FVectorType>(destType);
2575 unsigned size = resultBuffer.size();
2576 unsigned indexWidth =
2578 for (
size_t i = 0, e = std::min(srcVectorType.getNumElements(),
2579 destVectorType.getNumElements());
2581 auto iIdx = getOrCreateIntConstant(indexWidth, i);
2583 if (failed(recurse(arrayIndex, srcVectorType.getElementType(),
2584 destVectorType.getElementType())))
2587 SmallVector<Value> temp(resultBuffer.begin() + size,
2588 resultBuffer.end());
2590 resultBuffer.resize(size);
2591 resultBuffer.push_back(array);
2594 .Case<BundleType>([&](BundleType srcStructType) {
2595 auto destStructType = firrtl::type_cast<BundleType>(destType);
2596 unsigned size = resultBuffer.size();
2599 if (destStructType.getNumElements() != srcStructType.getNumElements())
2602 for (
auto elem :
llvm::enumerate(destStructType)) {
2603 auto structExtract =
2605 if (failed(recurse(structExtract,
2606 srcStructType.getElementType(elem.index()),
2607 destStructType.getElementType(elem.index()))))
2610 SmallVector<Value> temp(resultBuffer.begin() + size,
2611 resultBuffer.end());
2614 resultBuffer.resize(size);
2615 resultBuffer.push_back(newStruct);
2618 .Case<IntType>([&](
auto) {
2619 if (
auto result = cast(src, srcType, destType)) {
2620 resultBuffer.push_back(result);
2625 .Default([&](
auto) {
return failure(); });
2628 if (failed(recurse(array, sourceType, destType)))
2631 assert(resultBuffer.size() == 1 &&
2632 "resultBuffer must only contain a result array if `success` is true");
2633 return resultBuffer[0];
2641Value FIRRTLLowering::getLoweredAndExtendedValue(Value src, Type target) {
2642 auto srcType = cast<FIRRTLBaseType>(src.getType());
2643 auto dstType = cast<FIRRTLBaseType>(target);
2644 auto loweredSrc = getLoweredValue(src);
2647 auto dstWidth = dstType.getBitWidthOrSentinel();
2663 return getOrCreateIntConstant(dstWidth, 0);
2666 auto loweredSrcType = loweredSrc.getType();
2667 auto loweredDstType =
lowerType(dstType);
2670 if (loweredSrcType == loweredDstType)
2674 if (dstWidth == srcType.getBitWidthOrSentinel()) {
2676 if (loweredSrcType != loweredDstType &&
2677 (isa<hw::TypeAliasType>(loweredSrcType) ||
2678 isa<hw::TypeAliasType>(loweredDstType))) {
2679 return builder.createOrFold<
hw::BitcastOp>(loweredDstType, loweredSrc);
2684 if (isa<hw::ArrayType, hw::StructType>(loweredSrcType))
2685 return getExtOrTruncAggregateValue(loweredSrc, srcType, dstType,
2688 if (isa<seq::ClockType>(loweredSrcType)) {
2689 builder.emitError(
"cannot use clock type as an integer");
2693 auto intSourceType = dyn_cast<IntegerType>(loweredSrcType);
2694 if (!intSourceType) {
2695 builder.emitError(
"operand of type ")
2696 << loweredSrcType <<
" cannot be used as an integer";
2700 auto loweredSrcWidth = intSourceType.getWidth();
2701 if (loweredSrcWidth ==
unsigned(dstWidth))
2704 if (loweredSrcWidth >
unsigned(dstWidth)) {
2705 builder.emitError(
"operand should not be a truncation");
2710 auto valueFIRType = type_cast<FIRRTLBaseType>(src.getType()).getPassiveType();
2711 if (type_cast<IntType>(valueFIRType).isSigned())
2712 return comb::createOrFoldSExt(loweredSrc, loweredDstType, builder);
2714 auto zero = getOrCreateIntConstant(dstWidth - loweredSrcWidth, 0);
2723Value FIRRTLLowering::getLoweredAndExtOrTruncValue(Value value, Type destType) {
2724 assert(type_isa<FIRRTLBaseType>(value.getType()) &&
2725 type_isa<FIRRTLBaseType>(destType) &&
2726 "input/output value should be FIRRTL");
2729 auto destWidth = type_cast<FIRRTLBaseType>(destType).getBitWidthOrSentinel();
2730 if (destWidth == -1)
2733 auto result = getLoweredValue(value);
2745 return getOrCreateIntConstant(destWidth, 0);
2749 if (isa<hw::ArrayType, hw::StructType>(result.getType())) {
2751 if (destType == value.getType())
2754 return getExtOrTruncAggregateValue(
2755 result, type_cast<FIRRTLBaseType>(value.getType()),
2756 type_cast<FIRRTLBaseType>(destType),
2760 auto srcWidth = type_cast<IntegerType>(result.getType()).getWidth();
2761 if (srcWidth ==
unsigned(destWidth))
2767 if (srcWidth >
unsigned(destWidth)) {
2768 auto resultType = builder.getIntegerType(destWidth);
2772 auto resultType = builder.getIntegerType(destWidth);
2776 type_cast<FIRRTLBaseType>(value.getType()).getPassiveType();
2777 if (type_cast<IntType>(valueFIRType).isSigned())
2778 return comb::createOrFoldSExt(result, resultType, builder);
2780 auto zero = getOrCreateIntConstant(destWidth - srcWidth, 0);
2794std::optional<Value> FIRRTLLowering::getLoweredFmtOperand(Value operand) {
2796 if (type_isa<FStringType>(operand.getType())) {
2797 if (isa<TimeOp>(operand.getDefiningOp()))
2798 return sv::TimeOp::create(builder);
2799 if (isa<HierarchicalModuleNameOp>(operand.getDefiningOp()))
2803 auto loweredValue = getLoweredValue(operand);
2804 if (!loweredValue) {
2808 loweredValue = getOrCreateIntConstant(1, 0);
2813 if (
auto intTy = firrtl::type_cast<IntType>(operand.getType()))
2814 if (intTy.isSigned())
2815 loweredValue = sv::SystemFunctionOp::create(
2816 builder, loweredValue.getType(),
"signed", loweredValue);
2818 return loweredValue;
2822FIRRTLLowering::loweredFmtOperands(mlir::ValueRange operands,
2823 SmallVectorImpl<Value> &loweredOperands) {
2824 for (
auto operand : operands) {
2825 std::optional<Value> loweredValue = getLoweredFmtOperand(operand);
2830 loweredOperands.push_back(*loweredValue);
2835LogicalResult FIRRTLLowering::lowerStatementWithFd(
2836 const FileDescriptorInfo &fileDescriptor, Value clock, Value cond,
2837 const std::function<LogicalResult(Value)> &fn,
bool usePrintfCond) {
2839 bool failed =
false;
2840 circuitState.addMacroDecl(builder.getStringAttr(
"SYNTHESIS"));
2841 addToIfDefBlock(
"SYNTHESIS", std::function<
void()>(), [&]() {
2842 addToAlwaysBlock(clock, [&]() {
2845 circuitState.usedPrintf =
true;
2847 circuitState.addFragment(theModule,
"PRINTF_COND_FRAGMENT");
2850 Value ifCond = cond;
2851 if (usePrintfCond) {
2853 sv::MacroRefExprOp::create(builder, cond.getType(),
"PRINTF_COND_");
2854 ifCond = builder.createOrFold<
comb::AndOp>(ifCond, cond,
true);
2857 addIfProceduralBlock(ifCond, [&]() {
2861 if (fileDescriptor.isDefaultFd()) {
2863 fd = hw::ConstantOp::create(builder, APInt(32, 0x80000002));
2866 auto fdOrError = callFileDescriptorLib(fileDescriptor);
2867 if (llvm::failed(fdOrError)) {
2873 failed = llvm::failed(fn(fd));
2877 return failure(failed);
2881FIRRTLLowering::callFileDescriptorLib(
const FileDescriptorInfo &info) {
2882 circuitState.usedFileDescriptorLib =
true;
2883 circuitState.addFragment(theModule,
"CIRCT_LIB_LOGGING_FRAGMENT");
2886 if (
info.isSubstitutionRequired()) {
2887 SmallVector<Value> fileNameOperands;
2888 if (failed(loweredFmtOperands(
info.getSubstitutions(), fileNameOperands)))
2891 fileName = sv::SFormatFOp::create(builder,
info.getOutputFileFormat(),
2896 fileName = sv::ConstantStrOp::create(builder,
info.getOutputFileFormat())
2900 return sv::FuncCallProceduralOp::create(
2901 builder, mlir::TypeRange{builder.getIntegerType(32)},
2902 builder.getStringAttr(
"__circt_lib_logging::FileDescriptor::get"),
2903 ValueRange{fileName})
2913LogicalResult FIRRTLLowering::setLowering(Value orig, Value result) {
2914 if (
auto origType = dyn_cast<FIRRTLType>(orig.getType())) {
2915 assert((!result || !type_isa<FIRRTLType>(result.getType())) &&
2916 "Lowering didn't turn a FIRRTL value into a non-FIRRTL value");
2920 auto srcWidth = baseType.getPassiveType().getBitWidthOrSentinel();
2923 if (srcWidth != -1) {
2925 assert((srcWidth != 0) &&
2926 "Lowering produced value for zero width source");
2928 assert((srcWidth == 0) &&
2929 "Lowering produced null value but source wasn't zero width");
2933 assert(result &&
"Lowering of foreign type produced null value");
2936 auto &slot = valueMapping[orig];
2937 assert(!slot &&
"value lowered multiple times");
2944LogicalResult FIRRTLLowering::setPossiblyFoldedLowering(Value orig,
2948 if (
auto cst = dyn_cast_or_null<hw::ConstantOp>(result.getDefiningOp())) {
2949 auto &entry = hwConstantMap[cst.getValueAttr()];
2960 cst->moveBefore(&theModule.getBodyBlock()->front());
2964 return setLowering(orig, result);
2969template <
typename ResultOpType,
typename... CtorArgTypes>
2970LogicalResult FIRRTLLowering::setLoweringTo(Operation *orig,
2971 CtorArgTypes... args) {
2972 auto result = builder.createOrFold<ResultOpType>(args...);
2973 if (
auto *op = result.getDefiningOp())
2975 return setPossiblyFoldedLowering(orig->getResult(0), result);
2982template <
typename ResultOpType,
typename... CtorArgTypes>
2983LogicalResult FIRRTLLowering::setLoweringToLTL(Operation *orig,
2984 CtorArgTypes... args) {
2985 auto result = builder.createOrFold<ResultOpType>(args...);
2986 if (
auto *op = result.getDefiningOp())
2987 ltlOpFixupWorklist.insert(op);
2988 return setPossiblyFoldedLowering(orig->getResult(0), result);
2997Backedge FIRRTLLowering::createBackedge(Location loc, Type type) {
2998 auto backedge = backedgeBuilder.
get(type, loc);
2999 backedges.insert({backedge, backedge});
3007Backedge FIRRTLLowering::createBackedge(Value orig, Type type) {
3008 auto backedge = createBackedge(orig.getLoc(), type);
3009 (void)setLowering(orig, backedge);
3015bool FIRRTLLowering::updateIfBackedge(Value dest, Value src) {
3016 auto backedgeIt = backedges.find(dest);
3017 if (backedgeIt == backedges.end())
3019 backedgeIt->second = src;
3027void FIRRTLLowering::runWithInsertionPointAtEndOfBlock(
3028 const std::function<
void(
void)> &fn, Region ®ion) {
3032 auto oldIP = builder.saveInsertionPoint();
3034 builder.setInsertionPointToEnd(®ion.front());
3036 builder.restoreInsertionPoint(oldIP);
3040Value FIRRTLLowering::getReadValue(Value v) {
3041 Value result = readInOutCreated.lookup(v);
3047 auto oldIP = builder.saveInsertionPoint();
3048 if (
auto *vOp = v.getDefiningOp()) {
3049 builder.setInsertionPointAfter(vOp);
3053 builder.setInsertionPoint(&theModule.getBodyBlock()->front());
3058 if (
auto arrayIndexInout = v.getDefiningOp<sv::ArrayIndexInOutOp>()) {
3059 result = getReadValue(arrayIndexInout.getInput());
3061 arrayIndexInout.getIndex());
3066 builder.restoreInsertionPoint(oldIP);
3067 readInOutCreated.insert({v, result});
3071Value FIRRTLLowering::getNonClockValue(Value v) {
3072 auto it = fromClockMapping.try_emplace(v, Value{});
3074 ImplicitLocOpBuilder builder(v.getLoc(), v.getContext());
3075 builder.setInsertionPointAfterValue(v);
3076 it.first->second = seq::FromClockOp::create(builder, v);
3078 return it.first->second;
3081void FIRRTLLowering::addToAlwaysBlock(
3082 sv::EventControl clockEdge, Value clock, sv::ResetType resetStyle,
3083 sv::EventControl resetEdge, Value reset,
3084 const std::function<
void(
void)> &body,
3085 const std::function<
void(
void)> &resetBody) {
3086 AlwaysKeyType key{builder.getBlock(), clockEdge, clock,
3087 resetStyle, resetEdge, reset};
3088 sv::AlwaysOp alwaysOp;
3089 sv::IfOp insideIfOp;
3090 std::tie(alwaysOp, insideIfOp) = alwaysBlocks.lookup(key);
3094 assert(resetStyle != sv::ResetType::NoReset);
3107 auto createIfOp = [&]() {
3110 insideIfOp = sv::IfOp::create(
3111 builder, reset, [] {}, [] {});
3113 if (resetStyle == sv::ResetType::AsyncReset) {
3114 sv::EventControl events[] = {clockEdge, resetEdge};
3115 Value clocks[] = {clock, reset};
3117 alwaysOp = sv::AlwaysOp::create(builder, events, clocks, [&]() {
3118 if (resetEdge == sv::EventControl::AtNegEdge)
3119 llvm_unreachable(
"negative edge for reset is not expected");
3123 alwaysOp = sv::AlwaysOp::create(builder, clockEdge, clock, createIfOp);
3127 alwaysOp = sv::AlwaysOp::create(builder, clockEdge, clock);
3128 insideIfOp =
nullptr;
3130 alwaysBlocks[key] = {alwaysOp, insideIfOp};
3134 assert(insideIfOp &&
"reset body must be initialized before");
3135 runWithInsertionPointAtEndOfBlock(resetBody, insideIfOp.getThenRegion());
3136 runWithInsertionPointAtEndOfBlock(body, insideIfOp.getElseRegion());
3138 runWithInsertionPointAtEndOfBlock(body, alwaysOp.getBody());
3144 alwaysOp->moveBefore(builder.getInsertionBlock(),
3145 builder.getInsertionPoint());
3148LogicalResult FIRRTLLowering::emitGuards(Location loc,
3149 ArrayRef<Attribute> guards,
3150 std::function<
void(
void)>
emit) {
3151 if (guards.empty()) {
3155 auto guard = dyn_cast<StringAttr>(guards[0]);
3157 return mlir::emitError(loc,
3158 "elements in `guards` array must be `StringAttr`");
3161 circuitState.addMacroDecl(builder.getStringAttr(guard.getValue()));
3162 LogicalResult result = LogicalResult::failure();
3163 addToIfDefBlock(guard.getValue(), [&]() {
3164 result = emitGuards(loc, guards.drop_front(), emit);
3169void FIRRTLLowering::addToIfDefBlock(StringRef cond,
3170 std::function<
void(
void)> thenCtor,
3171 std::function<
void(
void)> elseCtor) {
3172 auto condAttr = builder.getStringAttr(cond);
3173 auto op = ifdefBlocks.lookup({builder.getBlock(), condAttr});
3175 runWithInsertionPointAtEndOfBlock(thenCtor, op.getThenRegion());
3176 runWithInsertionPointAtEndOfBlock(elseCtor, op.getElseRegion());
3181 op->moveBefore(builder.getInsertionBlock(), builder.getInsertionPoint());
3183 ifdefBlocks[{builder.getBlock(), condAttr}] =
3184 sv::IfDefOp::create(builder, condAttr, thenCtor, elseCtor);
3188void FIRRTLLowering::addToInitialBlock(std::function<
void(
void)> body) {
3189 auto op = initialBlocks.lookup(builder.getBlock());
3191 runWithInsertionPointAtEndOfBlock(body, op.getBody());
3196 op->moveBefore(builder.getInsertionBlock(), builder.getInsertionPoint());
3198 initialBlocks[builder.getBlock()] = sv::InitialOp::create(builder, body);
3202void FIRRTLLowering::addIfProceduralBlock(Value cond,
3203 std::function<
void(
void)> thenCtor,
3204 std::function<
void(
void)> elseCtor) {
3207 auto insertIt = builder.getInsertionPoint();
3208 if (insertIt != builder.getBlock()->begin())
3209 if (
auto ifOp = dyn_cast<sv::IfOp>(*--insertIt)) {
3210 if (ifOp.getCond() == cond) {
3211 runWithInsertionPointAtEndOfBlock(thenCtor, ifOp.getThenRegion());
3212 runWithInsertionPointAtEndOfBlock(elseCtor, ifOp.getElseRegion());
3217 sv::IfOp::create(builder, cond, thenCtor, elseCtor);
3229FIRRTLLowering::UnloweredOpResult
3230FIRRTLLowering::handleUnloweredOp(Operation *op) {
3232 if (!op->getRegions().empty() &&
3233 isa_and_nonnull<FIRRTLDialect>(op->getDialect())) {
3234 op->emitOpError(
"must explicitly handle its regions");
3235 return LoweringFailure;
3242 if (!isa_and_nonnull<FIRRTLDialect>(op->getDialect())) {
3244 for (
auto ®ion : op->getRegions())
3245 addToWorklist(region);
3246 for (
auto &operand : op->getOpOperands())
3247 if (auto lowered = getPossiblyInoutLoweredValue(operand.
get()))
3248 operand.set(lowered);
3249 for (
auto result : op->getResults())
3250 (void)setLowering(result, result);
3251 return AlreadyLowered;
3263 if (op->getNumResults() == 1) {
3264 auto resultType = op->getResult(0).getType();
3265 if (type_isa<FIRRTLBaseType>(resultType) &&
3267 (
isExpression(op) || isa<mlir::UnrealizedConversionCastOp>(op))) {
3269 (void)setLowering(op->getResult(0), Value());
3273 op->emitOpError(
"LowerToHW couldn't handle this operation");
3274 return LoweringFailure;
3277LogicalResult FIRRTLLowering::visitExpr(ConstantOp op) {
3280 return setLowering(op, Value());
3282 return setLowering(op, getOrCreateIntConstant(op.getValue()));
3285LogicalResult FIRRTLLowering::visitExpr(SpecialConstantOp op) {
3287 if (isa<ClockType>(op.getType())) {
3288 cst = getOrCreateClockConstant(op.getValue() ? seq::ClockConst::High
3289 :
seq::ClockConst::Low);
3291 cst = getOrCreateIntConstant(APInt( 1, op.getValue()));
3293 return setLowering(op, cst);
3296FailureOr<Value> FIRRTLLowering::lowerSubindex(SubindexOp op, Value input) {
3297 auto iIdx = getOrCreateIntConstant(
3299 firrtl::type_cast<FVectorType>(op.getInput().getType())
3306 if (isa<sv::InOutType>(input.getType()))
3307 result = builder.createOrFold<sv::ArrayIndexInOutOp>(input, iIdx);
3310 if (
auto *definingOp = result.getDefiningOp())
3315FailureOr<Value> FIRRTLLowering::lowerSubaccess(SubaccessOp op, Value input) {
3316 Value valueIdx = getLoweredAndExtOrTruncValue(
3318 UIntType::get(op->getContext(),
3320 firrtl::type_cast<FVectorType>(op.getInput().getType())
3321 .getNumElements())));
3323 op->emitError() <<
"input lowering failed";
3330 if (isa<sv::InOutType>(input.getType()))
3331 result = builder.createOrFold<sv::ArrayIndexInOutOp>(input, valueIdx);
3333 result = createArrayIndexing(input, valueIdx);
3334 if (
auto *definingOp = result.getDefiningOp())
3339FailureOr<Value> FIRRTLLowering::lowerSubfield(SubfieldOp op, Value input) {
3340 auto resultType =
lowerType(op->getResult(0).getType());
3341 if (!resultType || !input) {
3342 op->emitError() <<
"subfield type lowering failed";
3348 auto field = firrtl::type_cast<BundleType>(op.getInput().getType())
3349 .getElementName(op.getFieldIndex());
3351 if (isa<sv::InOutType>(input.getType()))
3352 result = builder.createOrFold<sv::StructFieldInOutOp>(input, field);
3355 if (
auto *definingOp = result.getDefiningOp())
3360LogicalResult FIRRTLLowering::visitExpr(SubindexOp op) {
3362 return setLowering(op, Value());
3364 auto input = getPossiblyInoutLoweredValue(op.getInput());
3366 return op.emitError() <<
"input lowering failed";
3368 auto result = lowerSubindex(op, input);
3371 return setLowering(op, *result);
3374LogicalResult FIRRTLLowering::visitExpr(SubaccessOp op) {
3376 return setLowering(op, Value());
3378 auto input = getPossiblyInoutLoweredValue(op.getInput());
3380 return op.emitError() <<
"input lowering failed";
3382 auto result = lowerSubaccess(op, input);
3385 return setLowering(op, *result);
3388LogicalResult FIRRTLLowering::visitExpr(SubfieldOp op) {
3391 if (getLoweredValue(op) || !op.getInput())
3395 return setLowering(op, Value());
3397 auto input = getPossiblyInoutLoweredValue(op.getInput());
3399 return op.emitError() <<
"input lowering failed";
3401 auto result = lowerSubfield(op, input);
3404 return setLowering(op, *result);
3407LogicalResult FIRRTLLowering::visitExpr(VectorCreateOp op) {
3408 auto resultType =
lowerType(op.getResult().getType());
3409 SmallVector<Value> operands;
3411 for (
auto oper :
llvm::reverse(op.getOperands())) {
3412 auto val = getLoweredValue(oper);
3415 operands.push_back(val);
3417 return setLoweringTo<hw::ArrayCreateOp>(op, resultType, operands);
3420LogicalResult FIRRTLLowering::visitExpr(BundleCreateOp op) {
3421 auto resultType =
lowerType(op.getResult().getType());
3422 SmallVector<Value> operands;
3423 for (
auto oper : op.getOperands()) {
3424 auto val = getLoweredValue(oper);
3427 operands.push_back(val);
3429 return setLoweringTo<hw::StructCreateOp>(op, resultType, operands);
3432LogicalResult FIRRTLLowering::visitExpr(FEnumCreateOp op) {
3435 return setLowering(op, Value());
3437 auto input = getLoweredValue(op.getInput());
3438 auto tagName = op.getFieldNameAttr();
3439 auto oldType = op.getType().base();
3441 auto element = *oldType.getElement(op.getFieldNameAttr());
3443 if (
auto structType = dyn_cast<hw::StructType>(newType)) {
3444 auto tagType = structType.getFieldType(
"tag");
3445 auto tagValue = IntegerAttr::get(tagType, element.value.getValue());
3446 auto tag = sv::LocalParamOp::create(builder, op.getLoc(), tagType, tagValue,
3448 auto bodyType = structType.getFieldType(
"body");
3449 auto body = hw::UnionCreateOp::create(builder, bodyType, tagName, input);
3450 SmallVector<Value> operands = {tag.getResult(), body.getResult()};
3451 return setLoweringTo<hw::StructCreateOp>(op, structType, operands);
3453 auto tagValue = IntegerAttr::get(newType, element.value.getValue());
3454 return setLoweringTo<sv::LocalParamOp>(op, newType, tagValue, tagName);
3457LogicalResult FIRRTLLowering::visitExpr(AggregateConstantOp op) {
3458 auto resultType =
lowerType(op.getResult().getType());
3460 getOrCreateAggregateConstantAttribute(op.getFieldsAttr(), resultType);
3462 return setLoweringTo<hw::AggregateConstantOp>(op, resultType,
3463 cast<ArrayAttr>(attr));
3466LogicalResult FIRRTLLowering::visitExpr(IsTagOp op) {
3468 auto tagName = op.getFieldNameAttr();
3469 auto lhs = getLoweredValue(op.getInput());
3470 if (isa<hw::StructType>(lhs.getType()))
3473 auto index = op.getFieldIndex();
3474 auto enumType = op.getInput().getType().base();
3475 auto tagValue = enumType.getElementValueAttr(index);
3476 auto tagValueType = IntegerType::get(op.getContext(), enumType.getTagWidth());
3477 auto loweredTagValue = IntegerAttr::get(tagValueType, tagValue.getValue());
3478 auto rhs = sv::LocalParamOp::create(builder, op.getLoc(), tagValueType,
3479 loweredTagValue, tagName);
3481 Type resultType = builder.getIntegerType(1);
3482 return setLoweringTo<comb::ICmpOp>(op, resultType, ICmpPredicate::eq, lhs,
3486LogicalResult FIRRTLLowering::visitExpr(SubtagOp op) {
3489 return setLowering(op, Value());
3491 auto tagName = op.getFieldNameAttr();
3492 auto input = getLoweredValue(op.getInput());
3494 return setLoweringTo<hw::UnionExtractOp>(op, field, tagName);
3497LogicalResult FIRRTLLowering::visitExpr(TagExtractOp op) {
3500 return setLowering(op, Value());
3502 auto input = getLoweredValue(op.getInput());
3508 if (isa<hw::StructType>(input.getType())) {
3509 return setLoweringTo<hw::StructExtractOp>(op, input,
"tag");
3514 return setLowering(op, input);
3521LogicalResult FIRRTLLowering::visitDecl(WireOp op) {
3522 auto origResultType = op.getResult().getType();
3526 if (!type_isa<FIRRTLType>(origResultType)) {
3527 createBackedge(op.getResult(), origResultType);
3531 auto resultType =
lowerType(origResultType);
3535 if (resultType.isInteger(0)) {
3536 if (op.getInnerSym())
3537 return op.emitError(
"zero width wire is referenced by name [")
3538 << *op.getInnerSym() <<
"] (e.g. in an XMR) but must be removed";
3539 return setLowering(op.getResult(), Value());
3543 auto innerSym = lowerInnerSymbol(op);
3544 auto name = op.getNameAttr();
3547 auto wire = hw::WireOp::create(
3548 builder, op.getLoc(), getOrCreateZConstant(resultType), name, innerSym);
3550 if (
auto svAttrs = sv::getSVAttributes(op))
3551 sv::setSVAttributes(wire, svAttrs);
3553 return setLowering(op.getResult(), wire);
3556LogicalResult FIRRTLLowering::visitDecl(VerbatimWireOp op) {
3557 auto resultTy =
lowerType(op.getType());
3560 resultTy = sv::InOutType::get(op.getContext(), resultTy);
3562 SmallVector<Value, 4> operands;
3563 operands.reserve(op.getSubstitutions().size());
3564 for (
auto operand : op.getSubstitutions()) {
3565 auto lowered = getLoweredValue(operand);
3568 operands.push_back(lowered);
3571 ArrayAttr symbols = op.getSymbolsAttr();
3573 symbols = ArrayAttr::get(op.getContext(), {});
3575 return setLoweringTo<sv::VerbatimExprSEOp>(op, resultTy, op.getTextAttr(),
3579LogicalResult FIRRTLLowering::visitDecl(NodeOp op) {
3580 auto operand = getLoweredValue(op.getInput());
3582 return handleZeroBit(op.getInput(), [&]() -> LogicalResult {
3583 if (op.getInnerSym())
3584 return op.emitError(
"zero width node is referenced by name [")
3585 << *op.getInnerSym()
3586 <<
"] (e.g. in an XMR) but must be "
3588 return setLowering(op.getResult(), Value());
3594 auto name = op.getNameAttr();
3595 auto innerSym = lowerInnerSymbol(op);
3598 operand = hw::WireOp::create(builder, operand, name, innerSym);
3601 if (
auto svAttrs = sv::getSVAttributes(op)) {
3603 operand = hw::WireOp::create(builder, operand, name);
3604 sv::setSVAttributes(operand.getDefiningOp(), svAttrs);
3607 return setLowering(op.getResult(), operand);
3610LogicalResult FIRRTLLowering::visitDecl(RegOp op) {
3611 auto resultType =
lowerType(op.getResult().getType());
3614 if (resultType.isInteger(0))
3615 return setLowering(op.getResult(), Value());
3617 Value clockVal = getLoweredValue(op.getClockVal());
3622 auto innerSym = lowerInnerSymbol(op);
3623 Backedge inputEdge = backedgeBuilder.
get(resultType);
3624 auto reg = seq::FirRegOp::create(builder, inputEdge, clockVal,
3625 op.getNameAttr(), innerSym);
3628 if (
auto randomRegister = op->getAttr(
"firrtl.random_init_register"))
3629 reg->setAttr(
"firrtl.random_init_register", randomRegister);
3630 if (
auto randomStart = op->getAttr(
"firrtl.random_init_start"))
3631 reg->setAttr(
"firrtl.random_init_start", randomStart);
3632 if (
auto randomEnd = op->getAttr(
"firrtl.random_init_end"))
3633 reg->setAttr(
"firrtl.random_init_end", randomEnd);
3636 if (
auto svAttrs = sv::getSVAttributes(op))
3637 sv::setSVAttributes(reg, svAttrs);
3640 (void)setLowering(op.getResult(),
reg);
3644LogicalResult FIRRTLLowering::visitDecl(RegResetOp op) {
3645 auto resultType =
lowerType(op.getResult().getType());
3648 if (resultType.isInteger(0))
3649 return setLowering(op.getResult(), Value());
3651 Value clockVal = getLoweredValue(op.getClockVal());
3652 Value resetSignal = getLoweredValue(op.getResetSignal());
3654 Value resetValue = getLoweredAndExtOrTruncValue(
3655 op.getResetValue(), type_cast<FIRRTLBaseType>(op.getResult().getType()));
3657 if (!clockVal || !resetSignal || !resetValue)
3661 auto innerSym = lowerInnerSymbol(op);
3662 bool isAsync = type_isa<AsyncResetType>(op.getResetSignal().getType());
3663 Backedge inputEdge = backedgeBuilder.
get(resultType);
3665 seq::FirRegOp::create(builder, inputEdge, clockVal, op.getNameAttr(),
3666 resetSignal, resetValue, innerSym, isAsync);
3669 if (
auto randomRegister = op->getAttr(
"firrtl.random_init_register"))
3670 reg->setAttr(
"firrtl.random_init_register", randomRegister);
3671 if (
auto randomStart = op->getAttr(
"firrtl.random_init_start"))
3672 reg->setAttr(
"firrtl.random_init_start", randomStart);
3673 if (
auto randomEnd = op->getAttr(
"firrtl.random_init_end"))
3674 reg->setAttr(
"firrtl.random_init_end", randomEnd);
3677 if (
auto svAttrs = sv::getSVAttributes(op))
3678 sv::setSVAttributes(reg, svAttrs);
3681 (void)setLowering(op.getResult(),
reg);
3686LogicalResult FIRRTLLowering::visitDecl(MemOp op) {
3689 if (type_isa<BundleType>(op.getDataType()))
3690 return op.emitOpError(
3691 "should have already been lowered from a ground type to an aggregate "
3692 "type using the LowerTypes pass. Use "
3693 "'firtool --lower-types' or 'circt-opt "
3694 "--pass-pipeline='firrtl.circuit(firrtl-lower-types)' "
3700 auto memType = seq::FirMemType::get(
3703 : std::optional<uint32_t>());
3705 seq::FirMemInitAttr memInit;
3706 if (
auto init = op.getInitAttr())
3707 memInit = seq::FirMemInitAttr::get(init.getContext(), init.getFilename(),
3708 init.getIsBinary(), init.getIsInline());
3710 auto memDecl = seq::FirMemOp::create(
3713 op.getInnerSymAttr(), memInit, op.getPrefixAttr(), Attribute{});
3716 if (
auto file = parent->getAttrOfType<hw::OutputFileAttr>(
"output_file")) {
3718 if (!file.isDirectory())
3719 dir = hw::OutputFileAttr::getAsDirectory(builder.getContext(),
3720 file.getDirectory());
3721 memDecl.setOutputFileAttr(dir);
3727 for (
size_t i = 0, e = op.getNumResults(); i != e; ++i) {
3729 auto addOutput = [&](StringRef field,
size_t width, Value value) {
3732 (void)setLowering(a, value);
3738 auto addInput = [&](StringRef field, Value backedge) {
3740 if (cast<FIRRTLBaseType>(a.getType())
3742 .getBitWidthOrSentinel() > 0)
3743 (void)setLowering(a, backedge);
3749 auto addInputPort = [&](StringRef field,
size_t width) -> Value {
3753 Value backedge, portValue;
3755 portValue = getOrCreateXConstant(1);
3757 auto portType = IntegerType::get(op.getContext(), width);
3758 backedge = portValue = createBackedge(builder.getLoc(), portType);
3760 addInput(field, backedge);
3764 auto addClock = [&](StringRef field) -> Value {
3765 Type clockTy = seq::ClockType::get(op.getContext());
3766 Value portValue = createBackedge(builder.getLoc(), clockTy);
3767 addInput(field, portValue);
3771 auto memportKind = op.getPortKind(i);
3772 if (memportKind == MemOp::PortKind::Read) {
3773 auto addr = addInputPort(
"addr", op.getAddrBits());
3774 auto en = addInputPort(
"en", 1);
3775 auto clk = addClock(
"clk");
3776 auto data = seq::FirMemReadOp::create(builder, memDecl,
addr,
clk,
en);
3778 }
else if (memportKind == MemOp::PortKind::ReadWrite) {
3779 auto addr = addInputPort(
"addr", op.getAddrBits());
3780 auto en = addInputPort(
"en", 1);
3781 auto clk = addClock(
"clk");
3784 auto mode = addInputPort(
"wmode", 1);
3786 mode = builder.createOrFold<
comb::AndOp>(mode, addInputPort(
"wmask", 1),
3793 auto rdata = seq::FirMemReadWriteOp::create(builder, memDecl,
addr,
clk,
3797 auto addr = addInputPort(
"addr", op.getAddrBits());
3800 auto en = addInputPort(
"en", 1);
3804 auto clk = addClock(
"clk");
3817LogicalResult FIRRTLLowering::visitDecl(InstanceOp oldInstance) {
3818 Operation *oldModule =
3819 oldInstance.getReferencedModule(circuitState.getInstanceGraph());
3821 auto *newModule = circuitState.getNewModule(oldModule);
3823 oldInstance->emitOpError(
"could not find module [")
3824 << oldInstance.getModuleName() <<
"] referenced by instance";
3830 ArrayAttr parameters;
3831 if (
auto oldExtModule = dyn_cast<FExtModuleOp>(oldModule))
3836 SmallVector<PortInfo, 8> portInfo = cast<FModuleLike>(oldModule).getPorts();
3841 for (
unsigned portIdx = 0, e = portInfo.size(); portIdx != e; ++portIdx)
3842 portIndicesByName[portInfo[portIdx].name] = portIdx;
3846 SmallVector<Value, 8> operands;
3847 for (
size_t portIndex = 0, e = portInfo.size(); portIndex != e; ++portIndex) {
3848 auto &port = portInfo[portIndex];
3851 oldInstance->emitOpError(
"could not lower type of port ") << port.name;
3856 if (portType.isInteger(0))
3860 if (port.isOutput())
3863 auto portResult = oldInstance.getResult(portIndex);
3864 assert(portResult &&
"invalid IR, couldn't find port");
3868 if (port.isInput()) {
3869 operands.push_back(createBackedge(portResult, portType));
3875 if (type_isa<AnalogType>(portResult.getType()) && portResult.hasOneUse()) {
3876 if (
auto attach = dyn_cast<AttachOp>(*portResult.getUsers().begin())) {
3878 auto loweredResult = getPossiblyInoutLoweredValue(source);
3879 operands.push_back(loweredResult);
3880 (void)setLowering(portResult, loweredResult);
3889 "." + port.getName().str() +
".wire");
3893 (void)setLowering(portResult, wire);
3895 operands.push_back(wire);
3902 auto innerSym = oldInstance.getInnerSymAttr();
3903 if (oldInstance.getLowerToBind()) {
3906 oldInstance.getContext(), oldInstance.getInnerSymAttr(), 0,
3909 auto bindOp = sv::BindOp::create(builder, theModule.getNameAttr(),
3910 innerSym.getSymName());
3913 if (
auto outputFile = oldInstance->getAttr(
"output_file"))
3914 bindOp->setAttr(
"output_file", outputFile);
3917 circuitState.addBind(bindOp);
3922 hw::InstanceOp::create(builder, newModule, oldInstance.getNameAttr(),
3923 operands, parameters, innerSym);
3925 if (oldInstance.getLowerToBind() || oldInstance.getDoNotPrint())
3926 newInstance.setDoNotPrintAttr(builder.getUnitAttr());
3928 if (newInstance.getInnerSymAttr())
3929 if (
auto forceName = circuitState.instanceForceNames.lookup(
3930 {newInstance->getParentOfType<hw::HWModuleOp>().getNameAttr(),
3931 newInstance.getInnerNameAttr()}))
3932 newInstance->setAttr(
"hw.verilogName", forceName);
3936 unsigned resultNo = 0;
3937 for (
size_t portIndex = 0, e = portInfo.size(); portIndex != e; ++portIndex) {
3938 auto &port = portInfo[portIndex];
3942 Value resultVal = newInstance.getResult(resultNo);
3944 auto oldPortResult = oldInstance.getResult(portIndex);
3945 (void)setLowering(oldPortResult, resultVal);
3951LogicalResult FIRRTLLowering::visitDecl(ContractOp oldOp) {
3952 SmallVector<Value> inputs;
3953 SmallVector<Type> types;
3954 for (
auto input : oldOp.getInputs()) {
3955 auto lowered = getLoweredValue(input);
3958 inputs.push_back(lowered);
3959 types.push_back(lowered.getType());
3962 auto newOp = verif::ContractOp::create(builder, types, inputs);
3963 newOp->setDiscardableAttrs(oldOp->getDiscardableAttrDictionary());
3964 auto &body = newOp.getBody().emplaceBlock();
3966 for (
auto [newResult, oldResult, oldArg] :
3967 llvm::zip(newOp.getResults(), oldOp.getResults(),
3968 oldOp.getBody().getArguments())) {
3969 if (failed(setLowering(oldResult, newResult)))
3971 if (failed(setLowering(oldArg, newResult)))
3975 body.getOperations().splice(body.end(),
3976 oldOp.getBody().front().getOperations());
3977 addToWorklist(body);
3987LogicalResult FIRRTLLowering::lowerNoopCast(Operation *op) {
3988 auto operand = getPossiblyInoutLoweredValue(op->getOperand(0));
3993 return setLowering(op->getResult(0), operand);
3996LogicalResult FIRRTLLowering::visitExpr(AsSIntPrimOp op) {
3997 if (isa<ClockType>(op.getInput().getType()))
3998 return setLowering(op->getResult(0),
3999 getLoweredNonClockValue(op.getInput()));
4000 return lowerNoopCast(op);
4003LogicalResult FIRRTLLowering::visitExpr(AsUIntPrimOp op) {
4004 if (isa<ClockType>(op.getInput().getType()))
4005 return setLowering(op->getResult(0),
4006 getLoweredNonClockValue(op.getInput()));
4007 return lowerNoopCast(op);
4010LogicalResult FIRRTLLowering::visitExpr(AsClockPrimOp op) {
4011 return setLoweringTo<seq::ToClockOp>(op, getLoweredValue(op.getInput()));
4014LogicalResult FIRRTLLowering::visitUnrealizedConversionCast(
4015 mlir::UnrealizedConversionCastOp op) {
4017 if (op.getNumOperands() != 1 || op.getNumResults() != 1)
4020 auto operand = op.getOperand(0);
4021 auto result = op.getResult(0);
4024 if (type_isa<FIRRTLType>(operand.getType()) &&
4025 type_isa<FIRRTLType>(result.getType()))
4026 return lowerNoopCast(op);
4030 if (!type_isa<FIRRTLType>(operand.getType())) {
4031 if (type_isa<FIRRTLType>(result.getType()))
4032 return setLowering(result, getPossiblyInoutLoweredValue(operand));
4038 auto loweredResult = getLoweredValue(operand);
4039 if (!loweredResult) {
4042 if (operand.getType().isSignlessInteger(0)) {
4043 return setLowering(result, Value());
4050 result.replaceAllUsesWith(loweredResult);
4054LogicalResult FIRRTLLowering::visitExpr(HWStructCastOp op) {
4057 if (
auto opStructType = dyn_cast<hw::StructType>(op.getOperand().getType()))
4058 return setLowering(op, op.getOperand());
4062 auto result = getLoweredValue(op.getOperand());
4068 op.replaceAllUsesWith(result);
4072LogicalResult FIRRTLLowering::visitExpr(BitCastOp op) {
4073 auto operand = getLoweredValue(op.getOperand());
4076 auto resultType =
lowerType(op.getType());
4080 return setLoweringTo<hw::BitcastOp>(op, resultType, operand);
4083LogicalResult FIRRTLLowering::visitExpr(CvtPrimOp op) {
4084 auto operand = getLoweredValue(op.getOperand());
4088 if (type_cast<IntType>(op.getOperand().getType()).isUnsigned())
4089 return setLowering(op, getOrCreateIntConstant(1, 0));
4091 return setLowering(op, Value());
4096 if (type_cast<IntType>(op.getOperand().getType()).isSigned())
4097 return setLowering(op, operand);
4100 auto zero = getOrCreateIntConstant(1, 0);
4101 return setLoweringTo<comb::ConcatOp>(op, zero, operand);
4104LogicalResult FIRRTLLowering::visitExpr(NotPrimOp op) {
4105 auto operand = getLoweredValue(op.getInput());
4109 auto allOnes = getOrCreateIntConstant(
4110 APInt::getAllOnes(operand.getType().getIntOrFloatBitWidth()));
4111 return setLoweringTo<comb::XorOp>(op, operand, allOnes,
true);
4114LogicalResult FIRRTLLowering::visitExpr(NegPrimOp op) {
4117 auto operand = getLoweredAndExtendedValue(op.getInput(), op.getType());
4121 auto resultType =
lowerType(op.getType());
4123 auto zero = getOrCreateIntConstant(resultType.getIntOrFloatBitWidth(), 0);
4124 return setLoweringTo<comb::SubOp>(op, zero, operand,
true);
4128LogicalResult FIRRTLLowering::visitExpr(PadPrimOp op) {
4129 auto operand = getLoweredAndExtendedValue(op.getInput(), op.getType());
4132 return setLowering(op, operand);
4135LogicalResult FIRRTLLowering::visitExpr(XorRPrimOp op) {
4136 auto operand = getLoweredValue(op.getInput());
4139 return setLowering(op, getOrCreateIntConstant(1, 0));
4144 return setLoweringTo<comb::ParityOp>(op, builder.getIntegerType(1), operand,
4148LogicalResult FIRRTLLowering::visitExpr(AndRPrimOp op) {
4149 auto operand = getLoweredValue(op.getInput());
4152 return setLowering(op, getOrCreateIntConstant(1, 1));
4157 return setLoweringTo<comb::ICmpOp>(
4158 op, ICmpPredicate::eq, operand,
4159 getOrCreateIntConstant(
4160 APInt::getAllOnes(operand.getType().getIntOrFloatBitWidth())),
4164LogicalResult FIRRTLLowering::visitExpr(OrRPrimOp op) {
4165 auto operand = getLoweredValue(op.getInput());
4168 return setLowering(op, getOrCreateIntConstant(1, 0));
4174 return setLoweringTo<comb::ICmpOp>(
4175 op, ICmpPredicate::ne, operand,
4176 getOrCreateIntConstant(operand.getType().getIntOrFloatBitWidth(), 0),
4184template <
typename ResultOpType>
4185LogicalResult FIRRTLLowering::lowerBinOpToVariadic(Operation *op) {
4186 auto resultType = op->getResult(0).getType();
4187 auto lhs = getLoweredAndExtendedValue(op->getOperand(0), resultType);
4188 auto rhs = getLoweredAndExtendedValue(op->getOperand(1), resultType);
4192 return setLoweringTo<ResultOpType>(op, lhs, rhs,
true);
4198template <
typename ResultOpType>
4199LogicalResult FIRRTLLowering::lowerElementwiseLogicalOp(Operation *op) {
4200 auto resultType = op->getResult(0).getType();
4201 auto lhs = getLoweredAndExtendedValue(op->getOperand(0), resultType);
4202 auto rhs = getLoweredAndExtendedValue(op->getOperand(1), resultType);
4213 auto intType = builder.getIntegerType(*bitwidth);
4214 auto retType = lhs.getType();
4217 auto result = builder.createOrFold<ResultOpType>(lhs, rhs,
true);
4218 return setLoweringTo<hw::BitcastOp>(op, retType, result);
4223template <
typename ResultUn
signedOpType,
typename ResultSignedOpType>
4224LogicalResult FIRRTLLowering::lowerBinOp(Operation *op) {
4226 auto resultType = op->getResult(0).getType();
4227 auto lhs = getLoweredAndExtendedValue(op->getOperand(0), resultType);
4228 auto rhs = getLoweredAndExtendedValue(op->getOperand(1), resultType);
4233 if (type_cast<IntType>(resultType).isSigned())
4234 return setLoweringTo<ResultSignedOpType>(op, lhs, rhs,
true);
4235 return setLoweringTo<ResultUnsignedOpType>(op, lhs, rhs,
true);
4240LogicalResult FIRRTLLowering::lowerCmpOp(Operation *op, ICmpPredicate signedOp,
4241 ICmpPredicate unsignedOp) {
4243 auto lhsIntType = type_cast<IntType>(op->getOperand(0).getType());
4244 auto rhsIntType = type_cast<IntType>(op->getOperand(1).getType());
4245 if (!lhsIntType.hasWidth() || !rhsIntType.hasWidth())
4249 if (cmpType.getWidth() == 0)
4250 cmpType = UIntType::get(builder.getContext(), 1);
4251 auto lhs = getLoweredAndExtendedValue(op->getOperand(0), cmpType);
4252 auto rhs = getLoweredAndExtendedValue(op->getOperand(1), cmpType);
4257 Type resultType = builder.getIntegerType(1);
4258 return setLoweringTo<comb::ICmpOp>(
4259 op, resultType, lhsIntType.isSigned() ? signedOp : unsignedOp, lhs, rhs,
4265template <
typename SignedOp,
typename Un
signedOp>
4266LogicalResult FIRRTLLowering::lowerDivLikeOp(Operation *op) {
4270 auto opType = type_cast<IntType>(op->getResult(0).getType());
4271 if (opType.getWidth() == 0)
4272 return setLowering(op->getResult(0), Value());
4276 auto lhs = getLoweredAndExtendedValue(op->getOperand(0), resultType);
4277 auto rhs = getLoweredAndExtendedValue(op->getOperand(1), resultType);
4282 if (opType.isSigned())
4283 result = builder.createOrFold<SignedOp>(lhs, rhs,
true);
4285 result = builder.createOrFold<UnsignedOp>(lhs, rhs,
true);
4287 if (
auto *definingOp = result.getDefiningOp())
4290 if (resultType == opType)
4291 return setLowering(op->getResult(0), result);
4292 return setLoweringTo<comb::ExtractOp>(op,
lowerType(opType), result, 0);
4295LogicalResult FIRRTLLowering::visitExpr(CatPrimOp op) {
4297 if (op.getInputs().empty())
4298 return setLowering(op, Value());
4300 SmallVector<Value> loweredOperands;
4303 for (
auto operand : op.getInputs()) {
4304 auto loweredOperand = getLoweredValue(operand);
4305 if (loweredOperand) {
4306 loweredOperands.push_back(loweredOperand);
4309 auto result =
handleZeroBit(operand, [&]() {
return success(); });
4317 if (loweredOperands.empty())
4318 return setLowering(op, Value());
4321 return setLoweringTo<comb::ConcatOp>(op, loweredOperands);
4328LogicalResult FIRRTLLowering::visitExpr(IsXIntrinsicOp op) {
4329 auto input = getLoweredNonClockValue(op.getArg());
4333 if (!isa<IntType>(input.getType())) {
4334 auto srcType = op.getArg().getType();
4336 assert(bitwidth &&
"Unknown width");
4337 auto intType = builder.getIntegerType(*bitwidth);
4338 input = builder.createOrFold<
hw::BitcastOp>(intType, input);
4341 return setLoweringTo<comb::ICmpOp>(
4342 op, ICmpPredicate::ceq, input,
4343 getOrCreateXConstant(input.getType().getIntOrFloatBitWidth()),
true);
4346LogicalResult FIRRTLLowering::visitStmt(FPGAProbeIntrinsicOp op) {
4347 auto operand = getLoweredValue(op.getInput());
4348 hw::WireOp::create(builder, operand);
4352LogicalResult FIRRTLLowering::visitExpr(PlusArgsTestIntrinsicOp op) {
4353 return setLoweringTo<sim::PlusArgsTestOp>(op, builder.getIntegerType(1),
4354 op.getFormatStringAttr());
4357LogicalResult FIRRTLLowering::visitExpr(PlusArgsValueIntrinsicOp op) {
4358 auto type =
lowerType(op.getResult().getType());
4362 auto valueOp = sim::PlusArgsValueOp::create(
4363 builder, builder.getIntegerType(1), type, op.getFormatStringAttr());
4364 if (failed(setLowering(op.getResult(), valueOp.getResult())))
4366 if (failed(setLowering(op.getFound(), valueOp.getFound())))
4371LogicalResult FIRRTLLowering::visitExpr(SizeOfIntrinsicOp op) {
4372 op.emitError(
"SizeOf should have been resolved.");
4376LogicalResult FIRRTLLowering::visitExpr(ClockGateIntrinsicOp op) {
4378 if (op.getTestEnable())
4379 testEnable = getLoweredValue(op.getTestEnable());
4380 return setLoweringTo<seq::ClockGateOp>(
4381 op, getLoweredValue(op.getInput()), getLoweredValue(op.getEnable()),
4382 testEnable, hw::InnerSymAttr{});
4385LogicalResult FIRRTLLowering::visitExpr(ClockInverterIntrinsicOp op) {
4386 auto operand = getLoweredValue(op.getInput());
4387 return setLoweringTo<seq::ClockInverterOp>(op, operand);
4390LogicalResult FIRRTLLowering::visitExpr(ClockDividerIntrinsicOp op) {
4391 auto operand = getLoweredValue(op.getInput());
4392 return setLoweringTo<seq::ClockDividerOp>(op, operand, op.getPow2());
4395LogicalResult FIRRTLLowering::visitExpr(LTLAndIntrinsicOp op) {
4396 return setLoweringToLTL<ltl::AndOp>(
4398 ValueRange{getLoweredValue(op.getLhs()), getLoweredValue(op.getRhs())});
4401LogicalResult FIRRTLLowering::visitExpr(LTLOrIntrinsicOp op) {
4402 return setLoweringToLTL<ltl::OrOp>(
4404 ValueRange{getLoweredValue(op.getLhs()), getLoweredValue(op.getRhs())});
4407LogicalResult FIRRTLLowering::visitExpr(LTLIntersectIntrinsicOp op) {
4408 return setLoweringToLTL<ltl::IntersectOp>(
4410 ValueRange{getLoweredValue(op.getLhs()), getLoweredValue(op.getRhs())});
4413LogicalResult FIRRTLLowering::visitExpr(LTLDelayIntrinsicOp op) {
4414 return setLoweringToLTL<ltl::DelayOp>(op, getLoweredValue(op.getInput()),
4415 op.getDelayAttr(), op.getLengthAttr());
4418LogicalResult FIRRTLLowering::visitExpr(LTLConcatIntrinsicOp op) {
4419 return setLoweringToLTL<ltl::ConcatOp>(
4421 ValueRange{getLoweredValue(op.getLhs()), getLoweredValue(op.getRhs())});
4424LogicalResult FIRRTLLowering::visitExpr(LTLRepeatIntrinsicOp op) {
4425 return setLoweringToLTL<ltl::RepeatOp>(op, getLoweredValue(op.getInput()),
4426 op.getBaseAttr(), op.getMoreAttr());
4429LogicalResult FIRRTLLowering::visitExpr(LTLGoToRepeatIntrinsicOp op) {
4430 return setLoweringToLTL<ltl::GoToRepeatOp>(
4431 op, getLoweredValue(op.getInput()), op.getBaseAttr(), op.getMoreAttr());
4434LogicalResult FIRRTLLowering::visitExpr(LTLNonConsecutiveRepeatIntrinsicOp op) {
4435 return setLoweringToLTL<ltl::NonConsecutiveRepeatOp>(
4436 op, getLoweredValue(op.getInput()), op.getBaseAttr(), op.getMoreAttr());
4439LogicalResult FIRRTLLowering::visitExpr(LTLNotIntrinsicOp op) {
4440 return setLoweringToLTL<ltl::NotOp>(op, getLoweredValue(op.getInput()));
4443LogicalResult FIRRTLLowering::visitExpr(LTLImplicationIntrinsicOp op) {
4444 return setLoweringToLTL<ltl::ImplicationOp>(
4446 ValueRange{getLoweredValue(op.getLhs()), getLoweredValue(op.getRhs())});
4449LogicalResult FIRRTLLowering::visitExpr(LTLUntilIntrinsicOp op) {
4450 return setLoweringToLTL<ltl::UntilOp>(
4452 ValueRange{getLoweredValue(op.getLhs()), getLoweredValue(op.getRhs())});
4455LogicalResult FIRRTLLowering::visitExpr(LTLEventuallyIntrinsicOp op) {
4456 return setLoweringToLTL<ltl::EventuallyOp>(op,
4457 getLoweredValue(op.getInput()));
4460LogicalResult FIRRTLLowering::visitExpr(LTLClockIntrinsicOp op) {
4461 return setLoweringToLTL<ltl::ClockOp>(op, getLoweredValue(op.getInput()),
4462 ltl::ClockEdge::Pos,
4463 getLoweredNonClockValue(op.getClock()));
4466template <
typename TargetOp,
typename IntrinsicOp>
4467LogicalResult FIRRTLLowering::lowerVerifIntrinsicOp(IntrinsicOp op) {
4468 auto property = getLoweredValue(op.getProperty());
4469 auto enable = op.getEnable() ? getLoweredValue(op.getEnable()) : Value();
4470 TargetOp::create(builder, property, enable, op.getLabelAttr());
4474LogicalResult FIRRTLLowering::visitStmt(VerifAssertIntrinsicOp op) {
4475 return lowerVerifIntrinsicOp<verif::AssertOp>(op);
4478LogicalResult FIRRTLLowering::visitStmt(VerifAssumeIntrinsicOp op) {
4479 return lowerVerifIntrinsicOp<verif::AssumeOp>(op);
4482LogicalResult FIRRTLLowering::visitStmt(VerifCoverIntrinsicOp op) {
4483 return lowerVerifIntrinsicOp<verif::CoverOp>(op);
4486LogicalResult FIRRTLLowering::visitStmt(VerifRequireIntrinsicOp op) {
4487 if (!isa<verif::ContractOp>(op->getParentOp()))
4488 return lowerVerifIntrinsicOp<verif::AssertOp>(op);
4489 return lowerVerifIntrinsicOp<verif::RequireOp>(op);
4492LogicalResult FIRRTLLowering::visitStmt(VerifEnsureIntrinsicOp op) {
4493 if (!isa<verif::ContractOp>(op->getParentOp()))
4494 return lowerVerifIntrinsicOp<verif::AssertOp>(op);
4495 return lowerVerifIntrinsicOp<verif::EnsureOp>(op);
4498LogicalResult FIRRTLLowering::visitExpr(HasBeenResetIntrinsicOp op) {
4499 auto clock = getLoweredNonClockValue(op.getClock());
4500 auto reset = getLoweredValue(op.getReset());
4501 if (!clock || !reset)
4503 auto resetType = op.getReset().getType();
4504 auto uintResetType = dyn_cast<UIntType>(resetType);
4505 auto isSync = uintResetType && uintResetType.getWidth() == 1;
4506 auto isAsync = isa<AsyncResetType>(resetType);
4507 if (!isAsync && !isSync) {
4508 auto d = op.emitError(
"uninferred reset passed to 'has_been_reset'; "
4509 "requires sync or async reset");
4510 d.attachNote() <<
"reset is of type " << resetType
4511 <<
", should be '!firrtl.uint<1>' or '!firrtl.asyncreset'";
4514 return setLoweringTo<verif::HasBeenResetOp>(op, clock, reset, isAsync);
4521LogicalResult FIRRTLLowering::visitExpr(BitsPrimOp op) {
4522 auto input = getLoweredValue(op.getInput());
4526 Type resultType = builder.getIntegerType(op.getHi() - op.getLo() + 1);
4527 return setLoweringTo<comb::ExtractOp>(op, resultType, input, op.getLo());
4530LogicalResult FIRRTLLowering::visitExpr(InvalidValueOp op) {
4531 auto resultTy =
lowerType(op.getType());
4538 if (type_isa<AnalogType>(op.getType()))
4541 return setLoweringTo<sv::WireOp>(op, resultTy,
".invalid_analog");
4544 if (type_cast<FIRRTLBaseType>(op.getType()).containsAnalog())
4555 auto constant = getOrCreateIntConstant(*bitwidth, 0);
4557 if (!type_isa<IntegerType>(resultTy))
4559 return setLowering(op, constant);
4563 op.emitOpError(
"unsupported type");
4567LogicalResult FIRRTLLowering::visitExpr(HeadPrimOp op) {
4568 auto input = getLoweredValue(op.getInput());
4571 auto inWidth = type_cast<IntegerType>(input.getType()).getWidth();
4572 if (op.getAmount() == 0)
4573 return setLowering(op, Value());
4574 Type resultType = builder.getIntegerType(op.getAmount());
4575 return setLoweringTo<comb::ExtractOp>(op, resultType, input,
4576 inWidth - op.getAmount());
4579LogicalResult FIRRTLLowering::visitExpr(ShlPrimOp op) {
4580 auto input = getLoweredValue(op.getInput());
4583 if (op.getAmount() == 0)
4585 return setLowering(op, getOrCreateIntConstant(op.getAmount(), 0));
4590 if (op.getAmount() == 0)
4591 return setLowering(op, input);
4593 auto zero = getOrCreateIntConstant(op.getAmount(), 0);
4594 return setLoweringTo<comb::ConcatOp>(op, input, zero);
4597LogicalResult FIRRTLLowering::visitExpr(ShrPrimOp op) {
4598 auto input = getLoweredValue(op.getInput());
4603 auto inWidth = type_cast<IntegerType>(input.getType()).getWidth();
4604 auto shiftAmount = op.getAmount();
4605 if (shiftAmount >= inWidth) {
4607 if (type_cast<IntType>(op.getInput().getType()).isUnsigned())
4608 return setLowering(op, {});
4611 shiftAmount = inWidth - 1;
4614 Type resultType = builder.getIntegerType(inWidth - shiftAmount);
4615 return setLoweringTo<comb::ExtractOp>(op, resultType, input, shiftAmount);
4618LogicalResult FIRRTLLowering::visitExpr(TailPrimOp op) {
4619 auto input = getLoweredValue(op.getInput());
4623 auto inWidth = type_cast<IntegerType>(input.getType()).getWidth();
4624 if (inWidth == op.getAmount())
4625 return setLowering(op, Value());
4626 Type resultType = builder.getIntegerType(inWidth - op.getAmount());
4627 return setLoweringTo<comb::ExtractOp>(op, resultType, input, 0);
4630LogicalResult FIRRTLLowering::visitExpr(MuxPrimOp op) {
4631 auto cond = getLoweredValue(op.getSel());
4632 auto ifTrue = getLoweredAndExtendedValue(op.getHigh(), op.getType());
4633 auto ifFalse = getLoweredAndExtendedValue(op.getLow(), op.getType());
4634 if (!cond || !ifTrue || !ifFalse)
4637 if (isa<ClockType>(op.getType()))
4638 return setLoweringTo<seq::ClockMuxOp>(op, cond, ifTrue, ifFalse);
4639 return setLoweringTo<comb::MuxOp>(op, ifTrue.getType(), cond, ifTrue, ifFalse,
4643LogicalResult FIRRTLLowering::visitExpr(Mux2CellIntrinsicOp op) {
4644 auto cond = getLoweredValue(op.getSel());
4645 auto ifTrue = getLoweredAndExtendedValue(op.getHigh(), op.getType());
4646 auto ifFalse = getLoweredAndExtendedValue(op.getLow(), op.getType());
4647 if (!cond || !ifTrue || !ifFalse)
4650 auto val = comb::MuxOp::create(builder, ifTrue.getType(), cond, ifTrue,
4652 return setLowering(op, createValueWithMuxAnnotation(val,
true));
4655LogicalResult FIRRTLLowering::visitExpr(Mux4CellIntrinsicOp op) {
4656 auto sel = getLoweredValue(op.getSel());
4657 auto v3 = getLoweredAndExtendedValue(op.getV3(), op.getType());
4658 auto v2 = getLoweredAndExtendedValue(op.getV2(), op.getType());
4659 auto v1 = getLoweredAndExtendedValue(op.getV1(), op.getType());
4660 auto v0 = getLoweredAndExtendedValue(op.getV0(), op.getType());
4661 if (!sel || !v3 || !v2 || !v1 || !v0)
4663 Value array[] = {v3, v2, v1, v0};
4666 return setLowering(op, createValueWithMuxAnnotation(val,
false));
4685Value FIRRTLLowering::createValueWithMuxAnnotation(Operation *op,
bool isMux2) {
4686 assert(op->getNumResults() == 1 &&
"only expect a single result");
4687 auto val = op->getResult(0);
4691 op, sv::SVAttributeAttr::get(builder.getContext(),
"cadence map_to_mux",
4698 OpBuilder::InsertionGuard guard(builder);
4699 builder.setInsertionPoint(op);
4700 StringRef namehint = isMux2 ?
"mux2cell_in" :
"mux4cell_in";
4701 for (
auto [idx, operand] :
llvm::enumerate(op->getOperands())) {
4703 op->getContext(),
nullptr, 0,
4706 hw::WireOp::create(builder, operand, namehint + Twine(idx), innerSym);
4707 op->setOperand(idx, wire);
4712 sv::setSVAttributes(assignOp,
4713 sv::SVAttributeAttr::get(builder.getContext(),
4714 "synopsys infer_mux_override",
4719Value FIRRTLLowering::createArrayIndexing(Value array, Value index) {
4721 auto size = hw::type_cast<hw::ArrayType>(array.getType()).getNumElements();
4726 if (!llvm::isPowerOf2_64(size)) {
4727 auto extElem = getOrCreateIntConstant(APInt(llvm::Log2_64_Ceil(size), 0));
4729 SmallVector<Value> temp(llvm::NextPowerOf2(size) - size, extValue);
4731 Value temp2[] = {ext.getResult(), array};
4737 return inBoundsRead;
4740LogicalResult FIRRTLLowering::visitExpr(MultibitMuxOp op) {
4742 auto index = getLoweredAndExtOrTruncValue(
4744 UIntType::get(op.getContext(),
4749 SmallVector<Value> loweredInputs;
4750 loweredInputs.reserve(op.getInputs().size());
4751 for (
auto input : op.getInputs()) {
4752 auto lowered = getLoweredAndExtendedValue(input, op.getType());
4755 loweredInputs.push_back(lowered);
4759 return setLowering(op, createArrayIndexing(array, index));
4762LogicalResult FIRRTLLowering::visitExpr(VerbatimExprOp op) {
4763 auto resultTy =
lowerType(op.getType());
4767 SmallVector<Value, 4> operands;
4768 operands.reserve(op.getSubstitutions().size());
4769 for (
auto operand : op.getSubstitutions()) {
4770 auto lowered = getLoweredValue(operand);
4773 operands.push_back(lowered);
4776 ArrayAttr symbols = op.getSymbolsAttr();
4778 symbols = ArrayAttr::get(op.getContext(), {});
4780 return setLoweringTo<sv::VerbatimExprOp>(op, resultTy, op.getTextAttr(),
4784LogicalResult FIRRTLLowering::visitExpr(XMRRefOp op) {
4788 Type baseType = op.getType().getType();
4791 if (isa<ClockType>(baseType))
4792 xmrType = builder.getIntegerType(1);
4796 return setLoweringTo<sv::XMRRefOp>(op, sv::InOutType::get(xmrType),
4797 op.getRef(), op.getVerbatimSuffixAttr());
4800LogicalResult FIRRTLLowering::visitExpr(XMRDerefOp op) {
4804 if (isa<ClockType>(op.getType()))
4805 xmrType = builder.getIntegerType(1);
4809 auto xmr = sv::XMRRefOp::create(builder, sv::InOutType::get(xmrType),
4810 op.getRef(), op.getVerbatimSuffixAttr());
4811 auto readXmr = getReadValue(xmr);
4812 if (!isa<ClockType>(op.getType()))
4813 return setLowering(op, readXmr);
4814 return setLoweringTo<seq::ToClockOp>(op, readXmr);
4819LogicalResult FIRRTLLowering::visitExpr(TimeOp op) {
return success(); }
4820LogicalResult FIRRTLLowering::visitExpr(HierarchicalModuleNameOp op) {
4828LogicalResult FIRRTLLowering::visitStmt(SkipOp op) {
4840FailureOr<bool> FIRRTLLowering::lowerConnect(Value destVal, Value srcVal) {
4841 auto srcType = srcVal.getType();
4842 auto dstType = destVal.getType();
4843 if (srcType != dstType &&
4844 (isa<hw::TypeAliasType>(srcType) || isa<hw::TypeAliasType>(dstType))) {
4847 return TypeSwitch<Operation *, FailureOr<bool>>(destVal.getDefiningOp())
4848 .Case<hw::WireOp>([&](
auto op) {
4849 maybeUnused(op.getInput());
4850 op.getInputMutable().assign(srcVal);
4853 .Case<seq::FirRegOp>([&](
auto op) {
4854 maybeUnused(op.getNext());
4855 op.getNextMutable().assign(srcVal);
4858 .Case<hw::StructExtractOp, hw::ArrayGetOp>([](
auto op) {
4861 op.emitOpError(
"used as connect destination");
4864 .Default([](
auto) {
return false; });
4867LogicalResult FIRRTLLowering::visitStmt(ConnectOp op) {
4868 auto dest = op.getDest();
4870 auto destType = type_cast<FIRRTLBaseType>(dest.getType()).getPassiveType();
4871 auto srcVal = getLoweredAndExtendedValue(op.getSrc(), destType);
4873 return handleZeroBit(op.getSrc(), []() { return success(); });
4875 auto destVal = getPossiblyInoutLoweredValue(dest);
4879 auto result = lowerConnect(destVal, srcVal);
4887 if (updateIfBackedge(destVal, srcVal))
4890 if (!isa<hw::InOutType>(destVal.getType()))
4891 return op.emitError(
"destination isn't an inout type");
4897LogicalResult FIRRTLLowering::visitStmt(MatchingConnectOp op) {
4898 auto dest = op.getDest();
4899 auto srcVal = getLoweredValue(op.getSrc());
4901 return handleZeroBit(op.getSrc(), []() { return success(); });
4903 auto destVal = getPossiblyInoutLoweredValue(dest);
4907 auto result = lowerConnect(destVal, srcVal);
4915 if (updateIfBackedge(destVal, srcVal))
4918 if (!isa<hw::InOutType>(destVal.getType()))
4919 return op.emitError(
"destination isn't an inout type");
4925LogicalResult FIRRTLLowering::visitStmt(ForceOp op) {
4926 auto srcVal = getLoweredValue(op.getSrc());
4930 auto destVal = getPossiblyInoutLoweredValue(op.getDest());
4934 if (!isa<hw::InOutType>(destVal.getType()))
4935 return op.emitError(
"destination isn't an inout type");
4938 circuitState.addMacroDecl(builder.getStringAttr(
"SYNTHESIS"));
4939 addToIfDefBlock(
"SYNTHESIS", std::function<
void()>(), [&]() {
4940 addToInitialBlock([&]() { sv::ForceOp::create(builder, destVal, srcVal); });
4945LogicalResult FIRRTLLowering::visitStmt(RefForceOp op) {
4946 auto src = getLoweredNonClockValue(op.getSrc());
4947 auto clock = getLoweredNonClockValue(op.getClock());
4948 auto pred = getLoweredValue(op.getPredicate());
4949 if (!src || !clock || !pred)
4952 auto destVal = getPossiblyInoutLoweredValue(op.getDest());
4957 circuitState.addMacroDecl(builder.getStringAttr(
"SYNTHESIS"));
4958 addToIfDefBlock(
"SYNTHESIS", std::function<
void()>(), [&]() {
4959 addToAlwaysBlock(clock, [&]() {
4960 addIfProceduralBlock(
4961 pred, [&]() { sv::ForceOp::create(builder, destVal, src); });
4966LogicalResult FIRRTLLowering::visitStmt(RefForceInitialOp op) {
4967 auto src = getLoweredNonClockValue(op.getSrc());
4968 auto pred = getLoweredValue(op.getPredicate());
4972 auto destVal = getPossiblyInoutLoweredValue(op.getDest());
4977 circuitState.addMacroDecl(builder.getStringAttr(
"SYNTHESIS"));
4978 addToIfDefBlock(
"SYNTHESIS", std::function<
void()>(), [&]() {
4979 addToInitialBlock([&]() {
4980 addIfProceduralBlock(
4981 pred, [&]() { sv::ForceOp::create(builder, destVal, src); });
4986LogicalResult FIRRTLLowering::visitStmt(RefReleaseOp op) {
4987 auto clock = getLoweredNonClockValue(op.getClock());
4988 auto pred = getLoweredValue(op.getPredicate());
4989 if (!clock || !pred)
4992 auto destVal = getPossiblyInoutLoweredValue(op.getDest());
4997 circuitState.addMacroDecl(builder.getStringAttr(
"SYNTHESIS"));
4998 addToIfDefBlock(
"SYNTHESIS", std::function<
void()>(), [&]() {
4999 addToAlwaysBlock(clock, [&]() {
5000 addIfProceduralBlock(pred,
5001 [&]() { sv::ReleaseOp::create(builder, destVal); });
5006LogicalResult FIRRTLLowering::visitStmt(RefReleaseInitialOp op) {
5007 auto destVal = getPossiblyInoutLoweredValue(op.getDest());
5008 auto pred = getLoweredValue(op.getPredicate());
5009 if (!destVal || !pred)
5013 circuitState.addMacroDecl(builder.getStringAttr(
"SYNTHESIS"));
5014 addToIfDefBlock(
"SYNTHESIS", std::function<
void()>(), [&]() {
5015 addToInitialBlock([&]() {
5016 addIfProceduralBlock(pred,
5017 [&]() { sv::ReleaseOp::create(builder, destVal); });
5025 StringRef originalFormatString,
5026 mlir::OperandRange operands,
5027 StringAttr &result) {
5030 SmallString<32> formatString;
5031 for (
size_t i = 0, e = originalFormatString.size(), subIdx = 0; i != e; ++i) {
5032 char c = originalFormatString[i];
5036 formatString.push_back(c);
5039 SmallString<6> width;
5040 c = originalFormatString[++i];
5043 c = originalFormatString[++i];
5054 formatString.append(width);
5060 formatString.push_back(c);
5067 if (originalFormatString.slice(i, i + 4) !=
"{{}}") {
5068 formatString.push_back(c);
5072 auto substitution = operands[subIdx++];
5073 assert(type_isa<FStringType>(substitution.getType()) &&
5074 "the operand for a '{{}}' substitution must be an 'fstring' type");
5076 TypeSwitch<Operation *, LogicalResult>(substitution.getDefiningOp())
5077 .template Case<TimeOp>([&](
auto) {
5078 formatString.append(
"%0t");
5081 .
template Case<HierarchicalModuleNameOp>([&](
auto) {
5082 formatString.append(
"%m");
5085 .Default([&](
auto) {
5086 emitError(loc,
"has a substitution with an unimplemented "
5088 .attachNote(substitution.getLoc())
5089 <<
"op with an unimplemented lowering is here";
5099 formatString.push_back(c);
5103 result = StringAttr::get(loc->getContext(), formatString);
5110LogicalResult FIRRTLLowering::visitPrintfLike(
5111 T op,
const FileDescriptorInfo &fileDescriptorInfo,
bool usePrintfCond) {
5112 auto clock = getLoweredNonClockValue(op.getClock());
5113 auto cond = getLoweredValue(op.getCond());
5114 if (!clock || !cond)
5117 StringAttr formatString;
5119 op.getSubstitutions(), formatString)))
5122 auto fn = [&](Value fd) {
5123 SmallVector<Value> operands;
5124 if (failed(loweredFmtOperands(op.getSubstitutions(), operands)))
5126 sv::FWriteOp::create(builder, op.getLoc(), fd, formatString, operands);
5130 return lowerStatementWithFd(fileDescriptorInfo, clock, cond, fn,
5134LogicalResult FIRRTLLowering::visitStmt(FPrintFOp op) {
5135 StringAttr outputFileAttr;
5137 op.getOutputFileSubstitutions(),
5141 FileDescriptorInfo outputFile(outputFileAttr,
5142 op.getOutputFileSubstitutions());
5143 return visitPrintfLike(op, outputFile,
false);
5147LogicalResult FIRRTLLowering::visitStmt(FFlushOp op) {
5148 auto clock = getLoweredNonClockValue(op.getClock());
5149 auto cond = getLoweredValue(op.getCond());
5150 if (!clock || !cond)
5153 auto fn = [&](Value fd) {
5154 sv::FFlushOp::create(builder, op.getLoc(), fd);
5158 if (!op.getOutputFileAttr())
5159 return lowerStatementWithFd({}, clock, cond, fn,
false);
5163 StringAttr outputFileAttr;
5165 op.getOutputFileSubstitutions(),
5169 return lowerStatementWithFd(
5170 FileDescriptorInfo(outputFileAttr, op.getOutputFileSubstitutions()),
5171 clock, cond, fn,
false);
5176LogicalResult FIRRTLLowering::visitStmt(StopOp op) {
5177 auto clock = getLoweredValue(op.getClock());
5178 auto cond = getLoweredValue(op.getCond());
5179 if (!clock || !cond)
5182 circuitState.usedStopCond =
true;
5183 circuitState.addFragment(theModule,
"STOP_COND_FRAGMENT");
5186 sv::MacroRefExprOp::create(builder, cond.getType(),
"STOP_COND_");
5187 Value exitCond = builder.createOrFold<
comb::AndOp>(stopCond, cond,
true);
5189 sim::ClockedTerminateOp::create(builder, clock, exitCond,
5190 op.getExitCode() == 0,
5199template <
typename... Args>
5201 StringRef opName, Args &&...args) {
5202 if (opName ==
"assert")
5203 return sv::AssertOp::create(builder, std::forward<Args>(args)...);
5204 if (opName ==
"assume")
5205 return sv::AssumeOp::create(builder, std::forward<Args>(args)...);
5206 if (opName ==
"cover")
5207 return sv::CoverOp::create(builder, std::forward<Args>(args)...);
5208 llvm_unreachable(
"unknown verification op");
5214template <
typename... Args>
5216 StringRef opName, Args &&...args) {
5217 if (opName ==
"assert")
5218 return sv::AssertConcurrentOp::create(builder, std::forward<Args>(args)...);
5219 if (opName ==
"assume")
5220 return sv::AssumeConcurrentOp::create(builder, std::forward<Args>(args)...);
5221 if (opName ==
"cover")
5222 return sv::CoverConcurrentOp::create(builder, std::forward<Args>(args)...);
5223 llvm_unreachable(
"unknown verification op");
5244LogicalResult FIRRTLLowering::lowerVerificationStatement(
5245 Operation *op, StringRef labelPrefix, Value opClock, Value opPredicate,
5246 Value opEnable, StringAttr opMessageAttr, ValueRange opOperands,
5247 StringAttr opNameAttr,
bool isConcurrent, EventControl opEventControl) {
5248 StringRef opName = op->getName().stripDialect();
5251 ArrayRef<Attribute> guards{};
5252 if (
auto guardsAttr = op->template getAttrOfType<ArrayAttr>(
"guards"))
5253 guards = guardsAttr.getValue();
5255 auto isCover = isa<CoverOp>(op);
5256 auto clock = getLoweredNonClockValue(opClock);
5257 auto enable = getLoweredValue(opEnable);
5258 auto predicate = getLoweredValue(opPredicate);
5259 if (!clock || !enable || !predicate)
5263 if (opNameAttr && !opNameAttr.getValue().empty())
5265 StringAttr prefixedLabel;
5268 StringAttr::get(builder.getContext(), labelPrefix + label.getValue());
5271 SmallVector<Value> messageOps;
5275 if (flavor == VerificationFlavor::IfElseFatal && !isa<AssertOp>(op))
5276 flavor = VerificationFlavor::None;
5278 if (flavor == VerificationFlavor::None) {
5282 auto format = op->getAttrOfType<StringAttr>(
"format");
5284 if (isConcurrent && format && format.getValue() ==
"ifElseFatal") {
5285 if (!isa<AssertOp>(op))
5286 return op->emitError()
5287 <<
"ifElseFatal format cannot be used for non-assertions";
5288 flavor = VerificationFlavor::IfElseFatal;
5289 }
else if (isConcurrent)
5290 flavor = VerificationFlavor::SVA;
5292 flavor = VerificationFlavor::Immediate;
5295 if (!isCover && opMessageAttr && !opMessageAttr.getValue().empty()) {
5296 message = opMessageAttr;
5297 if (failed(loweredFmtOperands(opOperands, messageOps)))
5300 if (flavor == VerificationFlavor::SVA) {
5305 for (
auto &loweredValue : messageOps)
5306 loweredValue =
sv::SampledOp::create(builder, loweredValue);
5312 case VerificationFlavor::Immediate: {
5314 auto deferImmediate = circt::sv::DeferAssertAttr::get(
5315 builder.getContext(), circt::sv::DeferAssert::Immediate);
5316 addToAlwaysBlock(clock, [&]() {
5317 addIfProceduralBlock(enable, [&]() {
5319 prefixedLabel, message, messageOps);
5324 case VerificationFlavor::IfElseFatal: {
5325 assert(isa<AssertOp>(op) &&
"only assert is expected");
5328 auto boolType = IntegerType::get(builder.getContext(), 1);
5329 predicate = comb::createOrFoldNot(predicate, builder,
true);
5330 predicate = builder.createOrFold<
comb::AndOp>(enable, predicate,
true);
5332 circuitState.addMacroDecl(builder.getStringAttr(
"SYNTHESIS"));
5333 addToIfDefBlock(
"SYNTHESIS", {}, [&]() {
5334 addToAlwaysBlock(clock, [&]() {
5335 addIfProceduralBlock(predicate, [&]() {
5336 circuitState.usedStopCond =
true;
5337 circuitState.addFragment(theModule,
"STOP_COND_FRAGMENT");
5339 circuitState.usedAssertVerboseCond =
true;
5340 circuitState.addFragment(theModule,
"ASSERT_VERBOSE_COND_FRAGMENT");
5342 addIfProceduralBlock(
5343 sv::MacroRefExprOp::create(builder, boolType,
5344 "ASSERT_VERBOSE_COND_"),
5345 [&]() { sv::ErrorOp::create(builder, message, messageOps); });
5346 addIfProceduralBlock(
5347 sv::MacroRefExprOp::create(builder, boolType,
"STOP_COND_"),
5348 [&]() { sv::FatalOp::create(builder); });
5354 case VerificationFlavor::SVA: {
5359 comb::createOrFoldNot(enable, builder,
true);
5361 builder.createOrFold<
comb::OrOp>(notEnable, predicate,
true);
5363 predicate = builder.createOrFold<
comb::AndOp>(enable, predicate,
true);
5367 sv::EventControl event;
5368 switch (opEventControl) {
5369 case EventControl::AtPosEdge:
5370 event = circt::sv::EventControl::AtPosEdge;
5372 case EventControl::AtEdge:
5373 event = circt::sv::EventControl::AtEdge;
5375 case EventControl::AtNegEdge:
5376 event = circt::sv::EventControl::AtNegEdge;
5382 circt::sv::EventControlAttr::get(builder.getContext(), event), clock,
5383 predicate, prefixedLabel, message, messageOps);
5386 case VerificationFlavor::None:
5388 "flavor `None` must be converted into one of concreate flavors");
5395 return emitGuards(op->getLoc(), guards,
emit);
5399LogicalResult FIRRTLLowering::visitStmt(AssertOp op) {
5400 return lowerVerificationStatement(
5401 op,
"assert__", op.getClock(), op.getPredicate(), op.getEnable(),
5402 op.getMessageAttr(), op.getSubstitutions(), op.getNameAttr(),
5403 op.getIsConcurrent(), op.getEventControl());
5407LogicalResult FIRRTLLowering::visitStmt(AssumeOp op) {
5408 return lowerVerificationStatement(
5409 op,
"assume__", op.getClock(), op.getPredicate(), op.getEnable(),
5410 op.getMessageAttr(), op.getSubstitutions(), op.getNameAttr(),
5411 op.getIsConcurrent(), op.getEventControl());
5415LogicalResult FIRRTLLowering::visitStmt(CoverOp op) {
5416 return lowerVerificationStatement(
5417 op,
"cover__", op.getClock(), op.getPredicate(), op.getEnable(),
5418 op.getMessageAttr(), op.getSubstitutions(), op.getNameAttr(),
5419 op.getIsConcurrent(), op.getEventControl());
5423LogicalResult FIRRTLLowering::visitStmt(UnclockedAssumeIntrinsicOp op) {
5428 auto guardsAttr = op->getAttrOfType<mlir::ArrayAttr>(
"guards");
5429 ArrayRef<Attribute> guards =
5430 guardsAttr ? guardsAttr.getValue() : ArrayRef<Attribute>();
5432 auto label = op.getNameAttr();
5433 StringAttr assumeLabel;
5434 if (label && !label.empty())
5436 StringAttr::get(builder.getContext(),
"assume__" + label.getValue());
5437 auto predicate = getLoweredValue(op.getPredicate());
5438 auto enable = getLoweredValue(op.getEnable());
5439 auto notEnable = comb::createOrFoldNot(enable, builder,
true);
5440 predicate = builder.createOrFold<
comb::OrOp>(notEnable, predicate,
true);
5442 SmallVector<Value> messageOps;
5443 for (
auto operand : op.getSubstitutions()) {
5444 auto loweredValue = getLoweredValue(operand);
5445 if (!loweredValue) {
5449 loweredValue = getOrCreateIntConstant(1, 0);
5451 messageOps.push_back(loweredValue);
5453 return emitGuards(op.getLoc(), guards, [&]() {
5454 sv::AlwaysOp::create(
5455 builder, ArrayRef(sv::EventControl::AtEdge), ArrayRef(predicate),
5457 if (op.getMessageAttr().getValue().empty())
5458 buildImmediateVerifOp(
5459 builder,
"assume", predicate,
5460 circt::sv::DeferAssertAttr::get(
5461 builder.getContext(), circt::sv::DeferAssert::Immediate),
5464 buildImmediateVerifOp(
5465 builder,
"assume", predicate,
5466 circt::sv::DeferAssertAttr::get(
5467 builder.getContext(), circt::sv::DeferAssert::Immediate),
5468 assumeLabel, op.getMessageAttr(), messageOps);
5473LogicalResult FIRRTLLowering::visitStmt(AttachOp op) {
5475 if (op.getAttached().size() < 2)
5478 SmallVector<Value, 4> inoutValues;
5479 for (
auto v : op.getAttached()) {
5480 inoutValues.push_back(getPossiblyInoutLoweredValue(v));
5481 if (!inoutValues.back()) {
5485 inoutValues.pop_back();
5489 if (!isa<hw::InOutType>(inoutValues.back().getType()))
5490 return op.emitError(
"operand isn't an inout type");
5493 if (inoutValues.size() < 2)
5504 bool isAttachInternalOnly =
5505 llvm::none_of(inoutValues, [](
auto v) {
return isa<BlockArgument>(v); });
5507 if (isAttachInternalOnly) {
5508 auto v0 = inoutValues.front();
5509 for (
auto v : inoutValues) {
5512 v.replaceAllUsesWith(v0);
5519 circuitState.addMacroDecl(builder.getStringAttr(
"SYNTHESIS"));
5520 circuitState.addMacroDecl(builder.getStringAttr(
"VERILATOR"));
5525 SmallVector<Value, 4> values;
5526 for (
auto inoutValue : inoutValues)
5527 values.push_back(getReadValue(inoutValue));
5529 for (
size_t i1 = 0, e = inoutValues.size(); i1 != e; ++i1) {
5530 for (
size_t i2 = 0; i2 != e; ++i2)
5538 sv::IfDefOp::create(
5539 builder,
"VERILATOR",
5541 sv::VerbatimOp::create(
5543 "`error \"Verilator does not support alias and thus "
5545 "arbitrarily connect bidirectional wires and ports\"");
5547 [&]() { sv::AliasOp::create(builder, inoutValues); });
5553LogicalResult FIRRTLLowering::visitStmt(BindOp op) {
5554 sv::BindOp::create(builder, op.getInstanceAttr());
5558LogicalResult FIRRTLLowering::fixupLTLOps() {
5559 if (ltlOpFixupWorklist.empty())
5561 LLVM_DEBUG(llvm::dbgs() <<
"Fixing up " << ltlOpFixupWorklist.size()
5565 for (
unsigned i = 0, e = ltlOpFixupWorklist.size(); i != e; ++i)
5566 for (
auto *user : ltlOpFixupWorklist[i]->getUsers())
5567 if (isa<
hw::WireOp>(user))
5568 ltlOpFixupWorklist.insert(user);
5571 while (!ltlOpFixupWorklist.empty()) {
5572 auto *op = ltlOpFixupWorklist.pop_back_val();
5575 if (
auto opIntf = dyn_cast_or_null<mlir::InferTypeOpInterface>(op)) {
5576 LLVM_DEBUG(llvm::dbgs() <<
"- Update " << *op <<
"\n");
5577 SmallVector<Type, 2> types;
5578 auto result = opIntf.inferReturnTypes(
5579 op->getContext(), op->getLoc(), op->getOperands(),
5580 op->getAttrDictionary(), op->getPropertiesStorage(), op->getRegions(),
5584 assert(types.size() == op->getNumResults());
5588 for (
auto [result, type] :
llvm::zip(op->getResults(), types)) {
5589 if (result.getType() == type)
5591 LLVM_DEBUG(llvm::dbgs()
5592 <<
" - Result #" << result.getResultNumber() <<
" from "
5593 << result.getType() <<
" to " << type <<
"\n");
5594 result.setType(type);
5595 for (
auto *user : result.getUsers())
5597 ltlOpFixupWorklist.insert(user);
5602 if (
auto wireOp = dyn_cast<hw::WireOp>(op)) {
5603 if (isa<ltl::SequenceType, ltl::PropertyType>(wireOp.getType())) {
5604 wireOp.replaceAllUsesWith(wireOp.getInput());
5605 LLVM_DEBUG(llvm::dbgs() <<
"- Remove " << wireOp <<
"\n");
5606 if (wireOp.use_empty())
5613 SmallPtrSet<Operation *, 4> usersReported;
5614 for (
auto *user : op->getUsers()) {
5615 if (!usersReported.insert(user).second)
5617 if (isa_and_nonnull<ltl::LTLDialect, verif::VerifDialect>(
5618 user->getDialect()))
5620 if (isa<hw::WireOp>(user))
5622 auto d = op->emitError(
5623 "verification operation used in a non-verification context");
5624 d.attachNote(user->getLoc())
5625 <<
"leaking outside verification context here";
assert(baseType &&"element must be base type")
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, mlir::OperandRange operands, StringAttr &result)
static Value getSingleNonInstanceOperand(AttachOp op)
static IntType getWidestIntType(Type t1, Type t2)
Given two FIRRTL integer types, return the widest one.
static FailureOr< VectorizeOp > lowerBody(VectorizeOp op)
Vectorizes the body of the given arc.vectorize operation if it is not already vectorized.
static Location getLoc(DefSlot slot)
static Block * getBodyBlock(FModuleLike mod)
std::shared_ptr< calyx::CalyxLoweringState > loweringState
Instantiate one of these and use it to build typed backedges.
void abandon()
Abandon the backedges, suppressing any diagnostics if they are still active upon destruction of the b...
Backedge get(mlir::Type resultType, mlir::LocationAttr optionalLoc={})
Create a typed backedge.
mlir::LogicalResult clearOrEmitError()
Clear the backedges, erasing any remaining cursor ops.
Backedge is a wrapper class around a Value.
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