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();
262 AnnotationSet(circuitOp).getAnnotation(testBenchDirAnnoClass)) {
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)
589 dontObfuscateModuleAnnoClass, noDedupAnnoClass,
593 markDUTAnnoClass, metadataDirAnnoClass,
594 elaborationArtefactsDirectoryAnnoClass, testBenchDirAnnoClass,
599 extractGrandCentralAnnoClass,
602 extractAssertionsAnnoClass, extractAssumptionsAnnoClass,
603 extractCoverageAnnoClass,
607 moduleHierarchyAnnoClass, testHarnessHierarchyAnnoClass,
608 blackBoxTargetDirAnnoClass))
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;
703 moveVerifAnno(getOperation(), circuitAnno, extractAssertionsAnnoClass,
704 "firrtl.extract.assert");
705 moveVerifAnno(getOperation(), circuitAnno, extractAssumptionsAnnoClass,
706 "firrtl.extract.assume");
707 moveVerifAnno(getOperation(), circuitAnno, extractCoverageAnnoClass,
708 "firrtl.extract.cover");
709 circuitAnno.removeAnnotationsWithClass(extractAssertionsAnnoClass,
710 extractAssumptionsAnnoClass,
711 extractCoverageAnnoClass);
713 state.processRemainingAnnotations(circuit, circuitAnno);
716 for (
auto &op : make_early_inc_range(circuitBody->getOperations())) {
718 TypeSwitch<Operation *, LogicalResult>(&op)
719 .Case<FModuleOp>([&](
auto module) {
720 auto loweredMod = lowerModule(module, topLevelModule, state);
724 state.recordModuleMapping(&op, loweredMod);
725 opsToProcess.push_back(loweredMod);
727 module.walk([&](Operation *op) {
728 for (auto res : op->getResults()) {
730 type_dyn_cast<BaseTypeAliasType>(res.getType()))
731 state.lowerType(aliasType, op->getLoc());
734 return lowerModulePortsAndMoveBody(module, loweredMod, state);
736 .Case<FExtModuleOp>([&](
auto extModule) {
738 lowerExtModule(extModule, topLevelModule, state);
741 state.recordModuleMapping(&op, loweredMod);
744 .Case<FMemModuleOp>([&](
auto memModule) {
746 lowerMemModule(memModule, topLevelModule, state);
749 state.recordModuleMapping(&op, loweredMod);
752 .Case<FormalOp>([&](
auto oldOp) {
753 auto builder = OpBuilder::atBlockEnd(topLevelModule);
754 auto newOp = verif::FormalOp::create(builder, oldOp.getLoc(),
756 oldOp.getParametersAttr());
757 newOp.getBody().emplaceBlock();
758 state.recordModuleMapping(oldOp, newOp);
759 opsToProcess.push_back(newOp);
762 .Case<SimulationOp>([&](
auto oldOp) {
763 auto loc = oldOp.getLoc();
764 auto builder = OpBuilder::atBlockEnd(topLevelModule);
765 auto newOp = verif::SimulationOp::create(
766 builder, loc, oldOp.getNameAttr(), oldOp.getParametersAttr());
767 auto &body = newOp.getRegion().emplaceBlock();
768 body.addArgument(seq::ClockType::get(builder.getContext()), loc);
769 body.addArgument(builder.getI1Type(), loc);
770 state.recordModuleMapping(oldOp, newOp);
771 opsToProcess.push_back(newOp);
774 .Case<emit::FileOp>([&](
auto fileOp) {
775 fileOp->moveBefore(topLevelModule, topLevelModule->end());
776 opsToProcess.push_back(fileOp);
779 .Default([&](Operation *op) {
784 op->moveBefore(topLevelModule, topLevelModule->end());
790 return signalPassFailure();
793 state.typeAliases.freeze();
798 SmallVector<Attribute> dutHierarchyFiles;
799 SmallVector<Attribute> testHarnessHierarchyFiles;
800 circuitAnno.removeAnnotations([&](
Annotation annotation) {
801 if (annotation.
isClass(moduleHierarchyAnnoClass)) {
802 auto file = hw::OutputFileAttr::getFromFilename(
804 annotation.
getMember<StringAttr>(
"filename").getValue(),
806 dutHierarchyFiles.push_back(file);
809 if (annotation.
isClass(testHarnessHierarchyAnnoClass)) {
810 auto file = hw::OutputFileAttr::getFromFilename(
812 annotation.
getMember<StringAttr>(
"filename").getValue(),
816 if (state.getTestHarness())
817 testHarnessHierarchyFiles.push_back(file);
819 dutHierarchyFiles.push_back(file);
825 if (!dutHierarchyFiles.empty())
826 state.getNewModule(state.getDut())
828 ArrayAttr::get(&getContext(), dutHierarchyFiles));
829 if (!testHarnessHierarchyFiles.empty())
830 state.getNewModule(state.getTestHarness())
832 ArrayAttr::get(&getContext(), testHarnessHierarchyFiles));
836 mlir::failableParallelForEach(&getContext(), opsToProcess, [&](
auto op) {
840 return signalPassFailure();
843 for (
auto bind : state.binds) {
848 for (
auto &[module, fragments] : state.fragments)
850 ArrayAttr::
get(&getContext(), fragments.getArrayRef()));
853 for (
auto oldNew : state.oldToNewModuleMap)
854 oldNew.first->erase();
856 if (!state.macroDeclNames.empty()) {
857 ImplicitLocOpBuilder b(UnknownLoc::get(&getContext()), circuit);
858 for (
auto name : state.macroDeclNames) {
859 sv::MacroDeclOp::create(b, name);
864 lowerFileHeader(circuit, state);
871void FIRRTLModuleLowering::lowerFileHeader(CircuitOp op,
872 CircuitLoweringState &state) {
875 ImplicitLocOpBuilder b(UnknownLoc::get(&getContext()), op);
879 auto emitGuardedDefine = [&](StringRef guard, StringRef defName,
880 StringRef defineTrue =
"",
881 StringRef defineFalse = StringRef()) {
882 if (!defineFalse.data()) {
883 assert(defineTrue.data() &&
"didn't define anything");
885 b, guard, [&]() { sv::MacroDefOp::create(b, defName, defineTrue); });
890 if (defineTrue.data())
891 sv::MacroDefOp::create(b, defName, defineTrue);
893 [&]() { sv::MacroDefOp::create(b, defName, defineFalse); });
898 auto emitGuard = [&](
const char *guard, llvm::function_ref<void(
void)> body) {
900 b, guard, [] {}, body);
903 if (state.usedFileDescriptorLib) {
905 SmallVector<hw::ModulePort> ports;
909 namePort.
name = b.getStringAttr(
"name");
910 namePort.
type = hw::StringType::get(b.getContext());
911 namePort.
dir = hw::ModulePort::Direction::Input;
912 ports.push_back(namePort);
916 fdPort.
name = b.getStringAttr(
"fd");
917 fdPort.
type = b.getIntegerType(32);
918 fdPort.
dir = hw::ModulePort::Direction::Output;
919 ports.push_back(fdPort);
922 auto moduleType = hw::ModuleType::get(b.getContext(), ports);
924 SmallVector<NamedAttribute> perArgumentsAttr;
925 perArgumentsAttr.push_back(
926 {sv::FuncOp::getExplicitlyReturnedAttrName(), b.getUnitAttr()});
928 SmallVector<Attribute> argumentAttr = {
929 DictionaryAttr::get(b.getContext(), {}),
930 DictionaryAttr::get(b.getContext(), perArgumentsAttr)};
933 auto func = sv::FuncOp::create(
935 "__circt_lib_logging::FileDescriptor::get", moduleType,
938 {b.getDictionaryAttr({}), b.getDictionaryAttr(perArgumentsAttr)}),
944 b.getStringAttr(
"__circt_lib_logging::FileDescriptor::get"));
947 sv::MacroDeclOp::create(b,
"__CIRCT_LIB_LOGGING");
949 emit::FragmentOp::create(b,
"CIRCT_LIB_LOGGING_FRAGMENT", [&] {
950 emitGuard(
"SYNTHESIS", [&]() {
951 emitGuard(
"__CIRCT_LIB_LOGGING", [&]() {
952 sv::VerbatimOp::create(b, R
"(// CIRCT Logging Library
953package __circt_lib_logging;
954 class FileDescriptor;
955 static int global_id [string];
956 static function int get(string name);
957 if (global_id.exists(name) == 32'h0) begin
958 global_id[name] = $fopen(name, "w");
959 if (global_id[name] == 32'h0)
960 $error("Failed to open file %s", name);
962 return global_id[name];
968 sv::MacroDefOp::create(b, "__CIRCT_LIB_LOGGING",
"");
974 if (state.usedPrintf) {
975 sv::MacroDeclOp::create(b,
"PRINTF_COND");
976 sv::MacroDeclOp::create(b,
"PRINTF_COND_");
977 emit::FragmentOp::create(b,
"PRINTF_COND_FRAGMENT", [&] {
978 sv::VerbatimOp::create(
979 b,
"\n// Users can define 'PRINTF_COND' to add an extra gate to "
981 emitGuard(
"PRINTF_COND_", [&]() {
982 emitGuardedDefine(
"PRINTF_COND",
"PRINTF_COND_",
"(`PRINTF_COND)",
"1");
987 if (state.usedAssertVerboseCond) {
988 sv::MacroDeclOp::create(b,
"ASSERT_VERBOSE_COND");
989 sv::MacroDeclOp::create(b,
"ASSERT_VERBOSE_COND_");
990 emit::FragmentOp::create(b,
"ASSERT_VERBOSE_COND_FRAGMENT", [&] {
991 sv::VerbatimOp::create(
992 b,
"\n// Users can define 'ASSERT_VERBOSE_COND' to add an extra "
993 "gate to assert error printing.");
994 emitGuard(
"ASSERT_VERBOSE_COND_", [&]() {
995 emitGuardedDefine(
"ASSERT_VERBOSE_COND",
"ASSERT_VERBOSE_COND_",
996 "(`ASSERT_VERBOSE_COND)",
"1");
1001 if (state.usedStopCond) {
1002 sv::MacroDeclOp::create(b,
"STOP_COND");
1003 sv::MacroDeclOp::create(b,
"STOP_COND_");
1004 emit::FragmentOp::create(b,
"STOP_COND_FRAGMENT", [&] {
1005 sv::VerbatimOp::create(
1006 b,
"\n// Users can define 'STOP_COND' to add an extra gate "
1007 "to stop conditions.");
1008 emitGuard(
"STOP_COND_", [&]() {
1009 emitGuardedDefine(
"STOP_COND",
"STOP_COND_",
"(`STOP_COND)",
"1");
1016FIRRTLModuleLowering::lowerPorts(ArrayRef<PortInfo> firrtlPorts,
1017 SmallVectorImpl<hw::PortInfo> &ports,
1018 Operation *moduleOp, StringRef moduleName,
1020 ports.reserve(firrtlPorts.size());
1022 size_t numResults = 0;
1023 for (
auto e :
llvm::enumerate(firrtlPorts)) {
1025 size_t portNo = e.index();
1030 if (firrtlPort.
sym.size() > 1 ||
1031 (firrtlPort.
sym.size() == 1 && !firrtlPort.
sym.getSymName()))
1032 return emitError(firrtlPort.
loc)
1033 <<
"cannot lower aggregate port " << firrtlPort.
name
1034 <<
" with field sensitive symbols, HW dialect does not support "
1035 "per field symbols yet.";
1036 hwPort.
setSym(firrtlPort.
sym, moduleOp->getContext());
1038 if (hadDontTouch && !hwPort.
getSym()) {
1039 if (hwPort.
type.isInteger(0)) {
1040 if (enableAnnotationWarning) {
1041 mlir::emitWarning(firrtlPort.
loc)
1042 <<
"zero width port " << hwPort.
name
1043 <<
" has dontTouch annotation, removing anyway";
1049 hw::InnerSymAttr::get(StringAttr::get(
1050 moduleOp->getContext(),
1051 Twine(
"__") + moduleName + Twine(
"__DONTTOUCH__") +
1052 Twine(portNo) + Twine(
"__") + firrtlPort.
name.strref())),
1053 moduleOp->getContext());
1058 moduleOp->emitError(
"cannot lower this port type to HW");
1064 if (hwPort.
type.isInteger(0)) {
1065 auto sym = hwPort.
getSym();
1066 if (sym && !sym.empty()) {
1067 return mlir::emitError(firrtlPort.
loc)
1068 <<
"zero width port " << hwPort.
name
1069 <<
" is referenced by name [" << sym
1070 <<
"] (e.g. in an XMR) but must be removed";
1077 hwPort.
dir = hw::ModulePort::Direction::Output;
1078 hwPort.
argNum = numResults++;
1079 }
else if (firrtlPort.
isInput()) {
1080 hwPort.
dir = hw::ModulePort::Direction::Input;
1081 hwPort.
argNum = numArgs++;
1085 hwPort.
type = hw::InOutType::get(hwPort.
type);
1086 hwPort.
dir = hw::ModulePort::Direction::InOut;
1087 hwPort.
argNum = numArgs++;
1089 hwPort.
loc = firrtlPort.
loc;
1090 ports.push_back(hwPort);
1100 auto params = llvm::map_range(module.getParameters(), [](Attribute a) {
1101 return cast<ParamDeclAttr>(a);
1106 Builder builder(module);
1111 SmallVector<Attribute> newParams;
1112 for (
const ParamDeclAttr &entry : params) {
1113 auto name = entry.getName();
1114 auto type = entry.getType();
1115 auto value = ignoreValues ? Attribute() : entry.getValue();
1117 hw::ParamDeclAttr::get(builder.getContext(), name, type, value);
1118 newParams.push_back(paramAttr);
1120 return builder.getArrayAttr(newParams);
1123bool FIRRTLModuleLowering::handleForceNameAnnos(
1126 bool failed =
false;
1129 if (!anno.
isClass(forceNameAnnoClass))
1132 auto sym = anno.
getMember<FlatSymbolRefAttr>(
"circt.nonlocal");
1139 auto diag = oldModule.emitOpError()
1140 <<
"contains a '" << forceNameAnnoClass
1141 <<
"' that is not a non-local annotation";
1142 diag.attachNote() <<
"the erroneous annotation is '" << anno.
getDict()
1153 auto diag = oldModule.emitOpError()
1154 <<
"contains a '" << forceNameAnnoClass
1155 <<
"' whose non-local symbol, '" << sym
1156 <<
"' does not exist in the circuit";
1157 diag.attachNote() <<
"the erroneous annotation is '" << anno.
getDict();
1170 cast<hw::InnerRefAttr>(nla.getNamepath().getValue().take_back(2)[0]);
1172 {{inst.getModule(), inst.getName()}, anno.
getMember(
"name")});
1173 if (!inserted.second &&
1174 (anno.
getMember(
"name") != (inserted.first->second))) {
1175 auto diag = oldModule.emitError()
1176 <<
"contained multiple '" << forceNameAnnoClass
1177 <<
"' with different names: " << inserted.first->second
1178 <<
" was not " << anno.
getMember(
"name");
1179 diag.attachNote() <<
"the erroneous annotation is '" << anno.
getDict()
1190 FExtModuleOp oldModule, Block *topLevelModule,
1201 SmallVector<PortInfo> firrtlPorts = oldModule.getPorts();
1202 SmallVector<hw::PortInfo, 8> ports;
1203 if (failed(lowerPorts(firrtlPorts, ports, oldModule, oldModule.getName(),
1208 StringRef verilogName;
1209 if (
auto defName = oldModule.getDefname())
1210 verilogName = defName.value();
1212 verilogName = oldModule.getName();
1214 auto builder = OpBuilder::atBlockEnd(topLevelModule);
1216 auto filesAttr = verbatimAnno.
getMember<ArrayAttr>(
"files");
1217 if (!filesAttr || filesAttr.empty()) {
1218 oldModule->emitError(
"VerbatimBlackBoxAnno missing or empty files array");
1223 auto primaryFile = cast<DictionaryAttr>(filesAttr[0]);
1224 auto primaryFileContent = primaryFile.getAs<StringAttr>(
"content");
1225 auto primaryOutputFile = primaryFile.getAs<StringAttr>(
"output_file");
1227 if (!primaryFileContent || !primaryOutputFile) {
1228 oldModule->emitError(
"VerbatimBlackBoxAnno file missing fields");
1232 auto primaryOutputFileAttr = hw::OutputFileAttr::getFromFilename(
1233 builder.getContext(), primaryOutputFile.getValue());
1235 auto primaryFileName = llvm::sys::path::filename(primaryOutputFile);
1236 auto verbatimSource =
loweringState.getVerbatimSourceForFile(primaryFileName);
1239 SmallVector<Attribute> additionalFiles;
1243 for (
size_t i = 1; i < filesAttr.size(); ++i) {
1244 auto file = cast<DictionaryAttr>(filesAttr[i]);
1245 auto content = file.getAs<StringAttr>(
"content");
1246 auto outputFile = file.getAs<StringAttr>(
"output_file");
1247 auto fileName = llvm::sys::path::filename(outputFile);
1249 if (!(content && outputFile)) {
1250 oldModule->emitError(
"VerbatimBlackBoxAnno file missing fields");
1258 auto fileSymbolName = circuitNamespace.newName(fileName);
1259 emitFile = emit::FileOp::create(builder, oldModule.getLoc(),
1260 outputFile.getValue(), fileSymbolName);
1261 builder.setInsertionPointToStart(&
emitFile.getBodyRegion().front());
1262 emit::VerbatimOp::create(builder, oldModule.getLoc(), content);
1263 builder.setInsertionPointAfter(
emitFile);
1266 auto ext = llvm::sys::path::extension(outputFile.getValue());
1267 bool excludeFromFileList = (ext ==
".h" || ext ==
".vh" || ext ==
".svh");
1268 auto outputFileAttr = hw::OutputFileAttr::getFromFilename(
1269 builder.getContext(), outputFile.getValue(), excludeFromFileList);
1270 emitFile->setAttr(
"output_file", outputFileAttr);
1274 additionalFiles.push_back(FlatSymbolRefAttr::get(
emitFile));
1280 parameters = builder.getArrayAttr({});
1282 if (!verbatimSource) {
1283 verbatimSource = sv::SVVerbatimSourceOp::create(
1284 builder, oldModule.getLoc(),
1285 circuitNamespace.newName(primaryFileName.str()),
1286 primaryFileContent.getValue(), primaryOutputFileAttr, parameters,
1287 additionalFiles.empty() ?
nullptr
1288 : builder.getArrayAttr(additionalFiles),
1289 builder.getStringAttr(verilogName));
1291 SymbolTable::setSymbolVisibility(
1292 verbatimSource, SymbolTable::getSymbolVisibility(oldModule));
1294 loweringState.registerVerbatimSource(primaryFileName, verbatimSource);
1297 return verbatimSource;
1301FIRRTLModuleLowering::lowerExtModule(FExtModuleOp oldModule,
1302 Block *topLevelModule,
1304 if (
auto verbatimMod =
1305 lowerVerbatimExtModule(oldModule, topLevelModule,
loweringState))
1311 SmallVector<PortInfo> firrtlPorts = oldModule.getPorts();
1312 SmallVector<hw::PortInfo, 8> ports;
1313 if (failed(lowerPorts(firrtlPorts, ports, oldModule, oldModule.getName(),
1317 StringRef verilogName;
1318 if (
auto defName = oldModule.getDefname())
1319 verilogName = defName.value();
1322 auto builder = OpBuilder::atBlockEnd(topLevelModule);
1323 auto nameAttr = builder.getStringAttr(oldModule.getName());
1328 auto newModule = hw::HWModuleExternOp::create(
1329 builder, oldModule.getLoc(), nameAttr, ports, verilogName, parameters);
1330 SymbolTable::setSymbolVisibility(newModule,
1331 SymbolTable::getSymbolVisibility(oldModule));
1333 bool hasOutputPort =
1334 llvm::any_of(firrtlPorts, [&](
auto p) {
return p.isOutput(); });
1335 if (!hasOutputPort &&
1337 internalVerifBlackBoxAnnoClass) &&
1339 newModule->setAttr(
"firrtl.extract.cover.extra", builder.getUnitAttr());
1342 if (
auto extReqs = oldModule.getExternalRequirements();
1343 extReqs && !extReqs.empty())
1344 newModule->setAttr(
"circt.external_requirements", extReqs);
1349 loweringState.processRemainingAnnotations(oldModule, annos);
1354 FExtModuleOp oldModule, Block *topLevelModule,
1359 auto verbatimSource =
1360 getVerbatimSourceForExtModule(oldModule, topLevelModule,
loweringState);
1362 if (!verbatimSource)
1365 SmallVector<PortInfo> firrtlPorts = oldModule.getPorts();
1366 SmallVector<hw::PortInfo, 8> ports;
1367 if (failed(lowerPorts(firrtlPorts, ports, oldModule, oldModule.getName(),
1371 StringRef verilogName;
1372 if (
auto defName = oldModule.getDefname())
1373 verilogName = defName.value();
1375 auto builder = OpBuilder::atBlockEnd(topLevelModule);
1377 auto newModule = sv::SVVerbatimModuleOp::create(
1380 builder.getStringAttr(oldModule.getName()),
1382 FlatSymbolRefAttr::get(verbatimSource),
1383 parameters ? parameters : builder.getArrayAttr({}),
1384 verilogName.empty() ? StringAttr{}
1385 : builder.getStringAttr(verilogName));
1387 SymbolTable::setSymbolVisibility(newModule,
1388 SymbolTable::getSymbolVisibility(oldModule));
1390 bool hasOutputPort =
1391 llvm::any_of(firrtlPorts, [&](
auto p) {
return p.isOutput(); });
1392 if (!hasOutputPort &&
1394 internalVerifBlackBoxAnnoClass) &&
1396 newModule->setAttr(
"firrtl.extract.cover.extra", builder.getUnitAttr());
1399 if (
auto extReqs = oldModule.getExternalRequirements();
1400 extReqs && !extReqs.empty())
1401 newModule->setAttr(
"circt.external_requirements", extReqs);
1406 loweringState.processRemainingAnnotations(oldModule, annos);
1411FIRRTLModuleLowering::lowerMemModule(FMemModuleOp oldModule,
1412 Block *topLevelModule,
1415 SmallVector<PortInfo> firrtlPorts = oldModule.getPorts();
1416 SmallVector<hw::PortInfo, 8> ports;
1417 if (failed(lowerPorts(firrtlPorts, ports, oldModule, oldModule.getName(),
1422 auto builder = OpBuilder::atBlockEnd(topLevelModule);
1423 auto newModule = hw::HWModuleExternOp::create(
1424 builder, oldModule.getLoc(), oldModule.getModuleNameAttr(), ports,
1425 oldModule.getModuleNameAttr());
1433FIRRTLModuleLowering::lowerModule(FModuleOp oldModule, Block *topLevelModule,
1436 SmallVector<PortInfo> firrtlPorts = oldModule.getPorts();
1437 SmallVector<hw::PortInfo, 8> ports;
1438 if (failed(lowerPorts(firrtlPorts, ports, oldModule, oldModule.getName(),
1443 auto builder = OpBuilder::atBlockEnd(topLevelModule);
1444 auto nameAttr = builder.getStringAttr(oldModule.getName());
1446 hw::HWModuleOp::create(builder, oldModule.getLoc(), nameAttr, ports);
1448 if (
auto comment = oldModule->getAttrOfType<StringAttr>(
"comment"))
1449 newModule.setCommentAttr(comment);
1452 SmallVector<StringRef, 13> attrNames = {
1453 "annotations",
"convention",
"layers",
1454 "portNames",
"sym_name",
"portDirections",
1455 "portTypes",
"portAnnotations",
"portSymbols",
1456 "portLocations",
"parameters", SymbolTable::getVisibilityAttrName(),
1459 DenseSet<StringRef> attrSet(attrNames.begin(), attrNames.end());
1460 SmallVector<NamedAttribute> newAttrs(newModule->getAttrs());
1462 llvm::make_filter_range(oldModule->getAttrs(), [&](auto namedAttr) {
1463 return !attrSet.count(namedAttr.getName()) &&
1464 !newModule->getAttrDictionary().contains(namedAttr.getName());
1466 newAttrs.push_back(i);
1468 newModule->setAttrs(newAttrs);
1472 SymbolTable::setSymbolVisibility(newModule,
1473 SymbolTable::getSymbolVisibility(oldModule));
1479 newModule->setAttr(
"firrtl.extract.cover.extra", builder.getUnitAttr());
1483 if (
auto testBenchDir =
loweringState.getTestBenchDirectory())
1485 if (!newModule->hasAttr(
"output_file"))
1486 newModule->setAttr(
"output_file", testBenchDir);
1487 newModule->setAttr(
"firrtl.extract.do_not_extract",
1488 builder.getUnitAttr());
1489 newModule.setCommentAttr(
1490 builder.getStringAttr(
"VCS coverage exclude_file"));
1496 loweringState.processRemainingAnnotations(oldModule, annos);
1505 Operation *insertPoint) {
1506 if (!value.hasOneUse())
1509 auto attach = dyn_cast<AttachOp>(*value.user_begin());
1510 if (!attach || attach.getNumOperands() != 2)
1514 auto loweredType =
lowerType(value.getType());
1515 if (loweredType.isInteger(0))
1520 auto attachedValue = attach.getOperand(attach.getOperand(0) == value);
1521 auto *op = attachedValue.getDefiningOp();
1522 if (op && op->getBlock() == insertPoint->getBlock() &&
1523 !op->isBeforeInBlock(insertPoint))
1528 ImplicitLocOpBuilder builder(insertPoint->getLoc(), insertPoint);
1544 if (type_isa<AnalogType>(flipValue.getType()))
1547 Operation *connectOp =
nullptr;
1548 for (
auto &use : flipValue.getUses()) {
1551 if (use.getOperandNumber() != 0)
1553 if (!isa<ConnectOp, MatchingConnectOp>(use.getOwner()))
1559 connectOp = use.getOwner();
1569 loweringState.lowerType(flipValue.getType(), flipValue.getLoc());
1570 if (loweredType.isInteger(0))
1575 ImplicitLocOpBuilder builder(insertPoint->getLoc(), insertPoint);
1577 auto connectSrc = connectOp->getOperand(1);
1580 if (!isa<FIRRTLType>(connectSrc.getType())) {
1586 if (!type_cast<FIRRTLBaseType>(connectSrc.getType()).isPassive())
1588 mlir::UnrealizedConversionCastOp::create(
1590 type_cast<FIRRTLBaseType>(connectSrc.getType()).getPassiveType(),
1596 auto destTy = type_cast<FIRRTLBaseType>(flipValue.getType()).getPassiveType();
1598 if (destTy != connectSrc.getType() &&
1599 (isa<BaseTypeAliasType>(connectSrc.getType()) ||
1600 isa<BaseTypeAliasType>(destTy))) {
1602 builder.createOrFold<BitCastOp>(flipValue.getType(), connectSrc);
1604 if (!destTy.isGround()) {
1606 if (destTy != type_cast<FIRRTLType>(connectSrc.getType()))
1608 }
else if (destTy.getBitWidthOrSentinel() !=
1609 type_cast<FIRRTLBaseType>(connectSrc.getType())
1610 .getBitWidthOrSentinel()) {
1613 auto destWidth = destTy.getBitWidthOrSentinel();
1614 assert(destWidth != -1 &&
"must know integer widths");
1615 connectSrc = builder.createOrFold<PadPrimOp>(destTy, connectSrc, destWidth);
1627 SmallVector<SubfieldOp> accesses;
1628 for (
auto *op : structValue.getUsers()) {
1629 assert(isa<SubfieldOp>(op));
1630 auto fieldAccess = cast<SubfieldOp>(op);
1632 fieldAccess.getInput().getType().base().getElementIndex(field);
1633 if (elemIndex && *elemIndex == fieldAccess.getFieldIndex())
1634 accesses.push_back(fieldAccess);
1642LogicalResult FIRRTLModuleLowering::lowerModulePortsAndMoveBody(
1645 ImplicitLocOpBuilder bodyBuilder(oldModule.getLoc(), newModule.getBody());
1652 bodyBuilder.setInsertionPoint(cursor);
1655 SmallVector<PortInfo> firrtlPorts = oldModule.getPorts();
1656 assert(oldModule.getBody().getNumArguments() == firrtlPorts.size() &&
1657 "port count mismatch");
1659 SmallVector<Value, 4> outputs;
1662 auto *outputOp = newModule.getBodyBlock()->getTerminator();
1663 ImplicitLocOpBuilder outputBuilder(oldModule.getLoc(), outputOp);
1665 unsigned nextHWInputArg = 0;
1666 int hwPortIndex = -1;
1667 for (
auto [firrtlPortIndex, port] :
llvm::enumerate(firrtlPorts)) {
1669 auto oldArg = oldModule.getBody().getArgument(firrtlPortIndex);
1672 type_isa<FIRRTLBaseType>(port.type) &&
1673 type_cast<FIRRTLBaseType>(port.type).getBitWidthOrSentinel() == 0;
1677 if (!port.isOutput() && !isZeroWidth) {
1680 Value newArg = newModule.getBody().getArgument(nextHWInputArg++);
1686 oldArg.replaceAllUsesWith(newArg);
1692 if (isZeroWidth && port.isInput()) {
1694 WireOp::create(bodyBuilder, port.type,
1695 "." + port.getName().str() +
".0width_input")
1697 oldArg.replaceAllUsesWith(newArg);
1705 outputs.push_back(value);
1706 assert(oldArg.use_empty() &&
"should have removed all uses of oldArg");
1712 auto newArg = WireOp::create(bodyBuilder, port.type,
1713 "." + port.getName().str() +
".output");
1716 oldArg.replaceAllUsesWith(newArg.getResult());
1719 auto resultHWType =
loweringState.lowerType(port.type, port.loc);
1720 if (!resultHWType.isInteger(0)) {
1723 outputs.push_back(output);
1726 if (
auto sym = newModule.getPort(hwPortIndex).getSym()) {
1727 newArg.setInnerSymAttr(sym);
1728 newModule.setPortSymbolAttr(hwPortIndex, {});
1734 outputOp->setOperands(outputs);
1737 auto &oldBlockInstList = oldModule.getBodyBlock()->getOperations();
1738 auto &newBlockInstList = newModule.getBodyBlock()->getOperations();
1739 newBlockInstList.splice(Block::iterator(cursor), oldBlockInstList,
1740 oldBlockInstList.begin(), oldBlockInstList.end());
1751FIRRTLModuleLowering::lowerFormalBody(verif::FormalOp newOp,
1753 auto builder = OpBuilder::atBlockEnd(&newOp.getBody().front());
1758 auto oldOp = cast<FormalOp>(
loweringState.getOldModule(newOp));
1759 auto moduleName = oldOp.getModuleNameAttr().getAttr();
1760 auto oldModule = cast<FModuleOp>(
1761 loweringState.getInstanceGraph().lookup(moduleName)->getModule());
1762 auto newModule = cast<hw::HWModuleOp>(
loweringState.getNewModule(oldModule));
1765 SmallVector<Value> symbolicInputs;
1766 for (
auto arg : newModule.getBody().getArguments())
1767 symbolicInputs.push_back(
1768 verif::SymbolicValueOp::create(builder, arg.
getLoc(), arg.getType()));
1771 hw::InstanceOp::create(builder, newOp.getLoc(), newModule,
1772 newModule.getNameAttr(), symbolicInputs);
1779FIRRTLModuleLowering::lowerSimulationBody(verif::SimulationOp newOp,
1781 auto builder = OpBuilder::atBlockEnd(newOp.getBody());
1784 auto oldOp = cast<SimulationOp>(
loweringState.getOldModule(newOp));
1785 auto moduleName = oldOp.getModuleNameAttr().getAttr();
1786 auto oldModule = cast<FModuleLike>(
1787 *
loweringState.getInstanceGraph().lookup(moduleName)->getModule());
1789 cast<hw::HWModuleLike>(
loweringState.getNewModule(oldModule));
1793 SmallVector<Value> inputs(newOp.getBody()->args_begin(),
1794 newOp.getBody()->args_end());
1795 auto instOp = hw::InstanceOp::create(builder, newOp.getLoc(), newModule,
1796 newModule.getNameAttr(), inputs);
1797 verif::YieldOp::create(builder, newOp.getLoc(), instOp.getResults());
1807struct FIRRTLLowering :
public FIRRTLVisitor<FIRRTLLowering, LogicalResult> {
1809 FIRRTLLowering(
hw::HWModuleOp module, CircuitLoweringState &circuitState)
1810 : theModule(module), circuitState(circuitState),
1811 builder(module.
getLoc(), module.getContext()), moduleNamespace(module),
1812 backedgeBuilder(builder, module.
getLoc()) {}
1814 LogicalResult
run();
1817 Value getOrCreateClockConstant(seq::ClockConst clock);
1818 Value getOrCreateIntConstant(
const APInt &value);
1819 Value getOrCreateIntConstant(
unsigned numBits, uint64_t val,
1820 bool isSigned =
false) {
1821 return getOrCreateIntConstant(APInt(numBits, val, isSigned));
1823 Attribute getOrCreateAggregateConstantAttribute(Attribute value, Type type);
1824 Value getOrCreateXConstant(
unsigned numBits);
1825 Value getOrCreateZConstant(Type type);
1826 Value getPossiblyInoutLoweredValue(Value value);
1827 Value getLoweredValue(Value value);
1828 Value getLoweredNonClockValue(Value value);
1829 Value getLoweredAndExtendedValue(Value value, Type destType);
1830 Value getLoweredAndExtOrTruncValue(Value value, Type destType);
1831 LogicalResult setLowering(Value orig, Value result);
1832 LogicalResult setPossiblyFoldedLowering(Value orig, Value result);
1833 template <
typename ResultOpType,
typename... CtorArgTypes>
1834 LogicalResult setLoweringTo(Operation *orig, CtorArgTypes... args);
1835 template <
typename ResultOpType,
typename... CtorArgTypes>
1836 LogicalResult setLoweringToLTL(Operation *orig, CtorArgTypes... args);
1837 Backedge createBackedge(Location loc, Type type);
1838 Backedge createBackedge(Value orig, Type type);
1839 bool updateIfBackedge(Value dest, Value src);
1842 bool requiresInnerSymbol(hw::InnerSymbolOpInterface op) {
1847 if (
auto forceable = dyn_cast<Forceable>(op.getOperation()))
1848 if (forceable.isForceable())
1856 hw::InnerSymAttr lowerInnerSymbol(hw::InnerSymbolOpInterface op) {
1857 auto attr = op.getInnerSymAttr();
1861 if (requiresInnerSymbol(op))
1863 op.getContext(), attr, 0,
1868 void runWithInsertionPointAtEndOfBlock(
const std::function<
void(
void)> &fn,
1872 Value getReadValue(Value v);
1874 Value getNonClockValue(Value v);
1876 void addToAlwaysBlock(sv::EventControl clockEdge, Value clock,
1877 sv::ResetType resetStyle, sv::EventControl resetEdge,
1878 Value reset,
const std::function<
void(
void)> &body = {},
1879 const std::function<void(
void)> &resetBody = {});
1880 void addToAlwaysBlock(Value clock,
1881 const std::function<
void(
void)> &body = {}) {
1882 addToAlwaysBlock(sv::EventControl::AtPosEdge, clock, sv::ResetType(),
1883 sv::EventControl(), Value(), body,
1884 std::function<
void(
void)>());
1887 LogicalResult emitGuards(Location loc, ArrayRef<Attribute> guards,
1888 std::function<
void(
void)>
emit);
1889 void addToIfDefBlock(StringRef cond, std::function<
void(
void)> thenCtor,
1890 std::function<
void(
void)> elseCtor = {});
1891 void addToInitialBlock(std::function<
void(
void)> body);
1892 void addIfProceduralBlock(Value cond, std::function<
void(
void)> thenCtor,
1893 std::function<
void(
void)> elseCtor = {});
1894 Value getExtOrTruncAggregateValue(Value array,
FIRRTLBaseType sourceType,
1896 bool allowTruncate);
1897 Value createArrayIndexing(Value array, Value index);
1898 Value createValueWithMuxAnnotation(Operation *op,
bool isMux2);
1900 using FIRRTLVisitor<FIRRTLLowering, LogicalResult>::visitExpr;
1901 using FIRRTLVisitor<FIRRTLLowering, LogicalResult>::visitDecl;
1902 using FIRRTLVisitor<FIRRTLLowering, LogicalResult>::visitStmt;
1905 enum UnloweredOpResult { AlreadyLowered, NowLowered, LoweringFailure };
1906 UnloweredOpResult handleUnloweredOp(Operation *op);
1907 LogicalResult visitExpr(ConstantOp op);
1908 LogicalResult visitExpr(SpecialConstantOp op);
1909 LogicalResult visitExpr(SubindexOp op);
1910 LogicalResult visitExpr(SubaccessOp op);
1911 LogicalResult visitExpr(SubfieldOp op);
1912 LogicalResult visitExpr(VectorCreateOp op);
1913 LogicalResult visitExpr(BundleCreateOp op);
1914 LogicalResult visitExpr(FEnumCreateOp op);
1915 LogicalResult visitExpr(AggregateConstantOp op);
1916 LogicalResult visitExpr(IsTagOp op);
1917 LogicalResult visitExpr(SubtagOp op);
1918 LogicalResult visitExpr(TagExtractOp op);
1921 if (
auto castOp = dyn_cast<mlir::UnrealizedConversionCastOp>(op))
1922 return visitUnrealizedConversionCast(castOp);
1927 LogicalResult visitDecl(WireOp op);
1928 LogicalResult visitDecl(NodeOp op);
1929 LogicalResult visitDecl(RegOp op);
1930 LogicalResult visitDecl(RegResetOp op);
1931 LogicalResult visitDecl(MemOp op);
1932 LogicalResult visitDecl(InstanceOp oldInstance);
1933 LogicalResult visitDecl(VerbatimWireOp op);
1934 LogicalResult visitDecl(ContractOp op);
1937 LogicalResult lowerNoopCast(Operation *op);
1938 LogicalResult visitExpr(AsSIntPrimOp op);
1939 LogicalResult visitExpr(AsUIntPrimOp op);
1940 LogicalResult visitExpr(AsClockPrimOp op);
1941 LogicalResult visitExpr(AsAsyncResetPrimOp op) {
return lowerNoopCast(op); }
1943 LogicalResult visitExpr(HWStructCastOp op);
1944 LogicalResult visitExpr(BitCastOp op);
1946 visitUnrealizedConversionCast(mlir::UnrealizedConversionCastOp op);
1947 LogicalResult visitExpr(CvtPrimOp op);
1948 LogicalResult visitExpr(NotPrimOp op);
1949 LogicalResult visitExpr(NegPrimOp op);
1950 LogicalResult visitExpr(PadPrimOp op);
1951 LogicalResult visitExpr(XorRPrimOp op);
1952 LogicalResult visitExpr(AndRPrimOp op);
1953 LogicalResult visitExpr(OrRPrimOp op);
1956 template <
typename ResultUnsignedOpType,
1957 typename ResultSignedOpType = ResultUnsignedOpType>
1958 LogicalResult lowerBinOp(Operation *op);
1959 template <
typename ResultOpType>
1960 LogicalResult lowerBinOpToVariadic(Operation *op);
1962 template <
typename ResultOpType>
1963 LogicalResult lowerElementwiseLogicalOp(Operation *op);
1965 LogicalResult lowerCmpOp(Operation *op, ICmpPredicate signedOp,
1966 ICmpPredicate unsignedOp);
1967 template <
typename SignedOp,
typename Un
signedOp>
1968 LogicalResult lowerDivLikeOp(Operation *op);
1970 LogicalResult visitExpr(CatPrimOp op);
1972 LogicalResult visitExpr(AndPrimOp op) {
1973 return lowerBinOpToVariadic<comb::AndOp>(op);
1975 LogicalResult visitExpr(OrPrimOp op) {
1976 return lowerBinOpToVariadic<comb::OrOp>(op);
1978 LogicalResult visitExpr(XorPrimOp op) {
1979 return lowerBinOpToVariadic<comb::XorOp>(op);
1981 LogicalResult visitExpr(ElementwiseOrPrimOp op) {
1982 return lowerElementwiseLogicalOp<comb::OrOp>(op);
1984 LogicalResult visitExpr(ElementwiseAndPrimOp op) {
1985 return lowerElementwiseLogicalOp<comb::AndOp>(op);
1987 LogicalResult visitExpr(ElementwiseXorPrimOp op) {
1988 return lowerElementwiseLogicalOp<comb::XorOp>(op);
1990 LogicalResult visitExpr(AddPrimOp op) {
1991 return lowerBinOpToVariadic<comb::AddOp>(op);
1993 LogicalResult visitExpr(EQPrimOp op) {
1994 return lowerCmpOp(op, ICmpPredicate::eq, ICmpPredicate::eq);
1996 LogicalResult visitExpr(NEQPrimOp op) {
1997 return lowerCmpOp(op, ICmpPredicate::ne, ICmpPredicate::ne);
1999 LogicalResult visitExpr(LTPrimOp op) {
2000 return lowerCmpOp(op, ICmpPredicate::slt, ICmpPredicate::ult);
2002 LogicalResult visitExpr(LEQPrimOp op) {
2003 return lowerCmpOp(op, ICmpPredicate::sle, ICmpPredicate::ule);
2005 LogicalResult visitExpr(GTPrimOp op) {
2006 return lowerCmpOp(op, ICmpPredicate::sgt, ICmpPredicate::ugt);
2008 LogicalResult visitExpr(GEQPrimOp op) {
2009 return lowerCmpOp(op, ICmpPredicate::sge, ICmpPredicate::uge);
2012 LogicalResult visitExpr(SubPrimOp op) {
return lowerBinOp<comb::SubOp>(op); }
2013 LogicalResult visitExpr(MulPrimOp op) {
2014 return lowerBinOpToVariadic<comb::MulOp>(op);
2016 LogicalResult visitExpr(DivPrimOp op) {
2017 return lowerDivLikeOp<comb::DivSOp, comb::DivUOp>(op);
2019 LogicalResult visitExpr(RemPrimOp op) {
2020 return lowerDivLikeOp<comb::ModSOp, comb::ModUOp>(op);
2024 LogicalResult visitExpr(IsXIntrinsicOp op);
2025 LogicalResult visitExpr(PlusArgsTestIntrinsicOp op);
2026 LogicalResult visitExpr(PlusArgsValueIntrinsicOp op);
2027 LogicalResult visitStmt(FPGAProbeIntrinsicOp op);
2028 LogicalResult visitExpr(ClockInverterIntrinsicOp op);
2029 LogicalResult visitExpr(ClockDividerIntrinsicOp op);
2030 LogicalResult visitExpr(SizeOfIntrinsicOp op);
2031 LogicalResult visitExpr(ClockGateIntrinsicOp op);
2032 LogicalResult visitExpr(LTLAndIntrinsicOp op);
2033 LogicalResult visitExpr(LTLOrIntrinsicOp op);
2034 LogicalResult visitExpr(LTLIntersectIntrinsicOp op);
2035 LogicalResult visitExpr(LTLDelayIntrinsicOp op);
2036 LogicalResult visitExpr(LTLConcatIntrinsicOp op);
2037 LogicalResult visitExpr(LTLRepeatIntrinsicOp op);
2038 LogicalResult visitExpr(LTLGoToRepeatIntrinsicOp op);
2039 LogicalResult visitExpr(LTLNonConsecutiveRepeatIntrinsicOp op);
2040 LogicalResult visitExpr(LTLNotIntrinsicOp op);
2041 LogicalResult visitExpr(LTLImplicationIntrinsicOp op);
2042 LogicalResult visitExpr(LTLUntilIntrinsicOp op);
2043 LogicalResult visitExpr(LTLEventuallyIntrinsicOp op);
2044 LogicalResult visitExpr(LTLClockIntrinsicOp op);
2046 template <
typename TargetOp,
typename IntrinsicOp>
2047 LogicalResult lowerVerifIntrinsicOp(IntrinsicOp op);
2048 LogicalResult visitStmt(VerifAssertIntrinsicOp op);
2049 LogicalResult visitStmt(VerifAssumeIntrinsicOp op);
2050 LogicalResult visitStmt(VerifCoverIntrinsicOp op);
2051 LogicalResult visitStmt(VerifRequireIntrinsicOp op);
2052 LogicalResult visitStmt(VerifEnsureIntrinsicOp op);
2053 LogicalResult visitExpr(HasBeenResetIntrinsicOp op);
2054 LogicalResult visitStmt(UnclockedAssumeIntrinsicOp op);
2057 LogicalResult visitExpr(BitsPrimOp op);
2058 LogicalResult visitExpr(InvalidValueOp op);
2059 LogicalResult visitExpr(HeadPrimOp op);
2060 LogicalResult visitExpr(ShlPrimOp op);
2061 LogicalResult visitExpr(ShrPrimOp op);
2062 LogicalResult visitExpr(DShlPrimOp op) {
2063 return lowerDivLikeOp<comb::ShlOp, comb::ShlOp>(op);
2065 LogicalResult visitExpr(DShrPrimOp op) {
2066 return lowerDivLikeOp<comb::ShrSOp, comb::ShrUOp>(op);
2068 LogicalResult visitExpr(DShlwPrimOp op) {
2069 return lowerDivLikeOp<comb::ShlOp, comb::ShlOp>(op);
2071 LogicalResult visitExpr(TailPrimOp op);
2072 LogicalResult visitExpr(MuxPrimOp op);
2073 LogicalResult visitExpr(Mux2CellIntrinsicOp op);
2074 LogicalResult visitExpr(Mux4CellIntrinsicOp op);
2075 LogicalResult visitExpr(MultibitMuxOp op);
2076 LogicalResult visitExpr(VerbatimExprOp op);
2077 LogicalResult visitExpr(XMRRefOp op);
2078 LogicalResult visitExpr(XMRDerefOp op);
2081 LogicalResult visitExpr(TimeOp op);
2082 LogicalResult visitExpr(HierarchicalModuleNameOp op);
2085 LogicalResult lowerVerificationStatement(
2086 Operation *op, StringRef labelPrefix, Value clock, Value predicate,
2087 Value enable, StringAttr messageAttr, ValueRange operands,
2088 StringAttr nameAttr,
bool isConcurrent, EventControl eventControl);
2090 LogicalResult visitStmt(SkipOp op);
2092 FailureOr<bool> lowerConnect(Value dest, Value srcVal);
2093 LogicalResult visitStmt(ConnectOp op);
2094 LogicalResult visitStmt(MatchingConnectOp op);
2095 LogicalResult visitStmt(ForceOp op);
2097 std::optional<Value> getLoweredFmtOperand(Value operand);
2098 LogicalResult loweredFmtOperands(ValueRange operands,
2099 SmallVectorImpl<Value> &loweredOperands);
2100 FailureOr<Value> callFileDescriptorLib(
const FileDescriptorInfo &info);
2104 LogicalResult lowerStatementWithFd(
2105 const FileDescriptorInfo &fileDescriptorInfo, Value clock, Value cond,
2106 const std::function<LogicalResult(Value)> &fn,
bool usePrintfCond);
2110 LogicalResult visitPrintfLike(T op,
2111 const FileDescriptorInfo &fileDescriptorInfo,
2112 bool usePrintfCond);
2113 LogicalResult visitStmt(PrintFOp op) {
return visitPrintfLike(op, {},
true); }
2114 LogicalResult visitStmt(FPrintFOp op);
2115 LogicalResult visitStmt(FFlushOp op);
2116 LogicalResult visitStmt(StopOp op);
2117 LogicalResult visitStmt(AssertOp op);
2118 LogicalResult visitStmt(AssumeOp op);
2119 LogicalResult visitStmt(CoverOp op);
2120 LogicalResult visitStmt(AttachOp op);
2121 LogicalResult visitStmt(RefForceOp op);
2122 LogicalResult visitStmt(RefForceInitialOp op);
2123 LogicalResult visitStmt(RefReleaseOp op);
2124 LogicalResult visitStmt(RefReleaseInitialOp op);
2125 LogicalResult visitStmt(BindOp op);
2127 FailureOr<Value> lowerSubindex(SubindexOp op, Value input);
2128 FailureOr<Value> lowerSubaccess(SubaccessOp op, Value input);
2129 FailureOr<Value> lowerSubfield(SubfieldOp op, Value input);
2131 LogicalResult fixupLTLOps();
2134 return circuitState.lowerType(type, builder.getLoc());
2142 CircuitLoweringState &circuitState;
2145 ImplicitLocOpBuilder builder;
2150 DenseMap<Value, Value> valueMapping;
2154 DenseMap<Value, Value> fromClockMapping;
2158 DenseMap<Attribute, Value> hwConstantMap;
2159 DenseMap<std::pair<Attribute, Type>, Attribute> hwAggregateConstantMap;
2163 DenseMap<unsigned, Value> hwConstantXMap;
2164 DenseMap<Type, Value> hwConstantZMap;
2170 DenseMap<Value, Value> readInOutCreated;
2173 DenseMap<StringAttr, sv::RegOp> fileNameToFileDescriptor;
2177 using AlwaysKeyType = std::tuple<
Block *, sv::EventControl, Value,
2178 sv::ResetType, sv::EventControl, Value>;
2195 llvm::MapVector<Value, Value> backedges;
2202 DenseSet<Operation *> maybeUnusedValues;
2204 void maybeUnused(Operation *op) { maybeUnusedValues.insert(op); }
2205 void maybeUnused(Value value) {
2206 if (
auto *op = value.getDefiningOp())
2218 SetVector<Operation *> ltlOpFixupWorklist;
2223 SmallVector<std::pair<Block::iterator, Block::iterator>> worklist;
2225 void addToWorklist(Block &block) {
2226 worklist.push_back({block.begin(), block.end()});
2228 void addToWorklist(Region ®ion) {
2229 for (
auto &block :
llvm::reverse(region))
2230 addToWorklist(block);
2241LogicalResult FIRRTLModuleLowering::lowerFileBody(emit::FileOp fileOp) {
2242 OpBuilder b(&getContext());
2243 fileOp->walk([&](Operation *op) {
2244 if (
auto bindOp = dyn_cast<BindOp>(op)) {
2245 b.setInsertionPointAfter(bindOp);
2246 sv::BindOp::create(b, bindOp.getLoc(), bindOp.getInstanceAttr());
2254FIRRTLModuleLowering::lowerBody(Operation *op,
2256 if (
auto moduleOp = dyn_cast<hw::HWModuleOp>(op))
2258 if (
auto formalOp = dyn_cast<verif::FormalOp>(op))
2260 if (
auto simulationOp = dyn_cast<verif::SimulationOp>(op))
2262 if (
auto fileOp = dyn_cast<emit::FileOp>(op))
2263 return lowerFileBody(fileOp);
2268LogicalResult FIRRTLLowering::run() {
2271 for (
auto arg : theModule.
getBodyBlock()->getArguments())
2272 if (failed(setLowering(arg, arg)))
2279 addToWorklist(theModule.getBody());
2280 SmallVector<Operation *, 16> opsToRemove;
2282 while (!worklist.empty()) {
2283 auto &[opsIt, opsEnd] = worklist.back();
2284 if (opsIt == opsEnd) {
2285 worklist.pop_back();
2288 Operation *op = &*opsIt++;
2290 builder.setInsertionPoint(op);
2291 builder.setLoc(op->getLoc());
2292 auto done = succeeded(dispatchVisitor(op));
2293 circuitState.processRemainingAnnotations(op,
AnnotationSet(op));
2295 opsToRemove.push_back(op);
2297 switch (handleUnloweredOp(op)) {
2298 case AlreadyLowered:
2301 opsToRemove.push_back(op);
2303 case LoweringFailure:
2315 for (
auto &[backedge, value] : backedges) {
2316 SmallVector<Location> driverLocs;
2322 if (backedge == value) {
2323 Location edgeLoc = backedge.getLoc();
2324 if (driverLocs.empty()) {
2325 mlir::emitError(edgeLoc,
"sink does not have a driver");
2327 auto diag = mlir::emitError(edgeLoc,
"sink in combinational loop");
2328 for (
auto loc : driverLocs)
2329 diag.attachNote(loc) <<
"through driver here";
2335 auto *it = backedges.find(value);
2336 if (it == backedges.end())
2339 driverLocs.push_back(value.getLoc());
2342 if (
auto *defOp = backedge.getDefiningOp())
2343 maybeUnusedValues.erase(defOp);
2344 backedge.replaceAllUsesWith(value);
2352 while (!opsToRemove.empty()) {
2353 auto *op = opsToRemove.pop_back_val();
2358 for (
auto result : op->getResults()) {
2362 auto builder = OpBuilder::atBlockBegin(theModule.getBodyBlock());
2364 builder.getIntegerType(0), 0);
2365 maybeUnusedValues.insert(zeroI0);
2367 result.replaceAllUsesWith(zeroI0);
2370 if (!op->use_empty()) {
2371 auto d = op->emitOpError(
2372 "still has uses; should remove ops in reverse order of visitation");
2373 SmallPtrSet<Operation *, 2> visited;
2374 for (
auto *user : op->getUsers())
2375 if (visited.insert(user).second)
2376 d.attachNote(user->
getLoc())
2377 <<
"used by " << user->
getName() <<
" op";
2380 maybeUnusedValues.erase(op);
2385 while (!maybeUnusedValues.empty()) {
2386 auto it = maybeUnusedValues.begin();
2388 maybeUnusedValues.erase(it);
2389 if (!isOpTriviallyDead(op))
2391 for (
auto operand : op->getOperands())
2392 if (auto *defOp = operand.getDefiningOp())
2393 maybeUnusedValues.insert(defOp);
2399 if (failed(fixupLTLOps()))
2410Value FIRRTLLowering::getOrCreateClockConstant(seq::ClockConst clock) {
2411 auto attr = seq::ClockConstAttr::get(theModule.getContext(), clock);
2413 auto &entry = hwConstantMap[attr];
2417 OpBuilder entryBuilder(&theModule.getBodyBlock()->front());
2418 entry = seq::ConstClockOp::create(entryBuilder, builder.getLoc(), attr);
2424Value FIRRTLLowering::getOrCreateIntConstant(
const APInt &value) {
2425 auto attr = builder.getIntegerAttr(
2426 builder.getIntegerType(value.getBitWidth()), value);
2428 auto &entry = hwConstantMap[attr];
2432 OpBuilder entryBuilder(&theModule.getBodyBlock()->front());
2439Attribute FIRRTLLowering::getOrCreateAggregateConstantAttribute(Attribute value,
2442 if (hw::type_isa<IntegerType>(type))
2443 return builder.getIntegerAttr(type, cast<IntegerAttr>(value).getValue());
2445 auto cache = hwAggregateConstantMap.lookup({value, type});
2450 SmallVector<Attribute> values;
2451 for (
auto e :
llvm::enumerate(cast<ArrayAttr>(value))) {
2453 if (
auto array = hw::type_dyn_cast<hw::ArrayType>(type))
2454 subType = array.getElementType();
2455 else if (
auto structType = hw::type_dyn_cast<hw::StructType>(type))
2456 subType = structType.getElements()[e.index()].type;
2458 assert(
false &&
"type must be either array or struct");
2460 values.push_back(getOrCreateAggregateConstantAttribute(e.value(), subType));
2464 if (hw::type_isa<hw::ArrayType>(type))
2465 std::reverse(values.begin(), values.end());
2467 auto &entry = hwAggregateConstantMap[{value, type}];
2468 entry = builder.getArrayAttr(values);
2476 const std::function<LogicalResult()> &fn) {
2477 assert(failedOperand &&
"Should be called on the failed operand");
2485Value FIRRTLLowering::getOrCreateXConstant(
unsigned numBits) {
2487 auto &entry = hwConstantXMap[numBits];
2491 OpBuilder entryBuilder(&theModule.getBodyBlock()->front());
2492 entry = sv::ConstantXOp::create(entryBuilder, builder.getLoc(),
2493 entryBuilder.getIntegerType(numBits));
2497Value FIRRTLLowering::getOrCreateZConstant(Type type) {
2498 auto &entry = hwConstantZMap[type];
2500 OpBuilder entryBuilder(&theModule.getBodyBlock()->front());
2501 entry = sv::ConstantZOp::create(entryBuilder, builder.getLoc(), type);
2510Value FIRRTLLowering::getPossiblyInoutLoweredValue(Value value) {
2512 if (
auto lowering = valueMapping.lookup(value)) {
2513 assert(!isa<FIRRTLType>(lowering.getType()) &&
2514 "Lowered value should be a non-FIRRTL value");
2523Value FIRRTLLowering::getLoweredValue(Value value) {
2524 auto result = getPossiblyInoutLoweredValue(value);
2530 if (isa<hw::InOutType>(result.getType()))
2531 return getReadValue(result);
2537Value FIRRTLLowering::getLoweredNonClockValue(Value value) {
2538 auto result = getLoweredValue(value);
2542 if (hw::type_isa<seq::ClockType>(result.getType()))
2543 return getNonClockValue(result);
2551Value FIRRTLLowering::getExtOrTruncAggregateValue(Value array,
2554 bool allowTruncate) {
2555 SmallVector<Value> resultBuffer;
2560 auto srcWidth = firrtl::type_cast<IntType>(sourceType).getWidthOrSentinel();
2561 auto destWidth = firrtl::type_cast<IntType>(destType).getWidthOrSentinel();
2562 auto resultType = builder.getIntegerType(destWidth);
2564 if (srcWidth == destWidth)
2567 if (srcWidth > destWidth) {
2571 builder.emitError(
"operand should not be a truncation");
2575 if (firrtl::type_cast<IntType>(sourceType).isSigned())
2576 return comb::createOrFoldSExt(value, resultType, builder);
2577 auto zero = getOrCreateIntConstant(destWidth - srcWidth, 0);
2585 return TypeSwitch<FIRRTLBaseType, LogicalResult>(srcType)
2586 .Case<FVectorType>([&](
auto srcVectorType) {
2587 auto destVectorType = firrtl::type_cast<FVectorType>(destType);
2588 unsigned size = resultBuffer.size();
2589 unsigned indexWidth =
2591 for (
size_t i = 0, e = std::min(srcVectorType.getNumElements(),
2592 destVectorType.getNumElements());
2594 auto iIdx = getOrCreateIntConstant(indexWidth, i);
2596 if (failed(recurse(arrayIndex, srcVectorType.getElementType(),
2597 destVectorType.getElementType())))
2600 SmallVector<Value> temp(resultBuffer.begin() + size,
2601 resultBuffer.end());
2603 resultBuffer.resize(size);
2604 resultBuffer.push_back(array);
2607 .Case<BundleType>([&](BundleType srcStructType) {
2608 auto destStructType = firrtl::type_cast<BundleType>(destType);
2609 unsigned size = resultBuffer.size();
2612 if (destStructType.getNumElements() != srcStructType.getNumElements())
2615 for (
auto elem :
llvm::enumerate(destStructType)) {
2616 auto structExtract =
2618 if (failed(recurse(structExtract,
2619 srcStructType.getElementType(elem.index()),
2620 destStructType.getElementType(elem.index()))))
2623 SmallVector<Value> temp(resultBuffer.begin() + size,
2624 resultBuffer.end());
2627 resultBuffer.resize(size);
2628 resultBuffer.push_back(newStruct);
2631 .Case<IntType>([&](
auto) {
2632 if (
auto result = cast(src, srcType, destType)) {
2633 resultBuffer.push_back(result);
2638 .Default([&](
auto) {
return failure(); });
2641 if (failed(recurse(array, sourceType, destType)))
2644 assert(resultBuffer.size() == 1 &&
2645 "resultBuffer must only contain a result array if `success` is true");
2646 return resultBuffer[0];
2654Value FIRRTLLowering::getLoweredAndExtendedValue(Value src, Type target) {
2655 auto srcType = cast<FIRRTLBaseType>(src.getType());
2656 auto dstType = cast<FIRRTLBaseType>(target);
2657 auto loweredSrc = getLoweredValue(src);
2660 auto dstWidth = dstType.getBitWidthOrSentinel();
2676 return getOrCreateIntConstant(dstWidth, 0);
2679 auto loweredSrcType = loweredSrc.getType();
2680 auto loweredDstType =
lowerType(dstType);
2683 if (loweredSrcType == loweredDstType)
2687 if (dstWidth == srcType.getBitWidthOrSentinel()) {
2689 if (loweredSrcType != loweredDstType &&
2690 (isa<hw::TypeAliasType>(loweredSrcType) ||
2691 isa<hw::TypeAliasType>(loweredDstType))) {
2692 return builder.createOrFold<
hw::BitcastOp>(loweredDstType, loweredSrc);
2697 if (isa<hw::ArrayType, hw::StructType>(loweredSrcType))
2698 return getExtOrTruncAggregateValue(loweredSrc, srcType, dstType,
2701 if (isa<seq::ClockType>(loweredSrcType)) {
2702 builder.emitError(
"cannot use clock type as an integer");
2706 auto intSourceType = dyn_cast<IntegerType>(loweredSrcType);
2707 if (!intSourceType) {
2708 builder.emitError(
"operand of type ")
2709 << loweredSrcType <<
" cannot be used as an integer";
2713 auto loweredSrcWidth = intSourceType.getWidth();
2714 if (loweredSrcWidth ==
unsigned(dstWidth))
2717 if (loweredSrcWidth >
unsigned(dstWidth)) {
2718 builder.emitError(
"operand should not be a truncation");
2723 auto valueFIRType = type_cast<FIRRTLBaseType>(src.getType()).getPassiveType();
2724 if (type_cast<IntType>(valueFIRType).isSigned())
2725 return comb::createOrFoldSExt(loweredSrc, loweredDstType, builder);
2727 auto zero = getOrCreateIntConstant(dstWidth - loweredSrcWidth, 0);
2736Value FIRRTLLowering::getLoweredAndExtOrTruncValue(Value value, Type destType) {
2737 assert(type_isa<FIRRTLBaseType>(value.getType()) &&
2738 type_isa<FIRRTLBaseType>(destType) &&
2739 "input/output value should be FIRRTL");
2742 auto destWidth = type_cast<FIRRTLBaseType>(destType).getBitWidthOrSentinel();
2743 if (destWidth == -1)
2746 auto result = getLoweredValue(value);
2758 return getOrCreateIntConstant(destWidth, 0);
2762 if (isa<hw::ArrayType, hw::StructType>(result.getType())) {
2764 if (destType == value.getType())
2767 return getExtOrTruncAggregateValue(
2768 result, type_cast<FIRRTLBaseType>(value.getType()),
2769 type_cast<FIRRTLBaseType>(destType),
2773 auto srcWidth = type_cast<IntegerType>(result.getType()).getWidth();
2774 if (srcWidth ==
unsigned(destWidth))
2780 if (srcWidth >
unsigned(destWidth)) {
2781 auto resultType = builder.getIntegerType(destWidth);
2785 auto resultType = builder.getIntegerType(destWidth);
2789 type_cast<FIRRTLBaseType>(value.getType()).getPassiveType();
2790 if (type_cast<IntType>(valueFIRType).isSigned())
2791 return comb::createOrFoldSExt(result, resultType, builder);
2793 auto zero = getOrCreateIntConstant(destWidth - srcWidth, 0);
2807std::optional<Value> FIRRTLLowering::getLoweredFmtOperand(Value operand) {
2809 if (type_isa<FStringType>(operand.getType())) {
2810 if (isa<TimeOp>(operand.getDefiningOp()))
2811 return sv::TimeOp::create(builder);
2812 if (isa<HierarchicalModuleNameOp>(operand.getDefiningOp()))
2816 auto loweredValue = getLoweredValue(operand);
2817 if (!loweredValue) {
2821 loweredValue = getOrCreateIntConstant(1, 0);
2826 if (
auto intTy = firrtl::type_cast<IntType>(operand.getType()))
2827 if (intTy.isSigned())
2828 loweredValue = sv::SystemFunctionOp::create(
2829 builder, loweredValue.getType(),
"signed", loweredValue);
2831 return loweredValue;
2835FIRRTLLowering::loweredFmtOperands(mlir::ValueRange operands,
2836 SmallVectorImpl<Value> &loweredOperands) {
2837 for (
auto operand : operands) {
2838 std::optional<Value> loweredValue = getLoweredFmtOperand(operand);
2843 loweredOperands.push_back(*loweredValue);
2848LogicalResult FIRRTLLowering::lowerStatementWithFd(
2849 const FileDescriptorInfo &fileDescriptor, Value clock, Value cond,
2850 const std::function<LogicalResult(Value)> &fn,
bool usePrintfCond) {
2852 bool failed =
false;
2853 circuitState.addMacroDecl(builder.getStringAttr(
"SYNTHESIS"));
2854 addToIfDefBlock(
"SYNTHESIS", std::function<
void()>(), [&]() {
2855 addToAlwaysBlock(clock, [&]() {
2858 circuitState.usedPrintf =
true;
2860 circuitState.addFragment(theModule,
"PRINTF_COND_FRAGMENT");
2863 Value ifCond = cond;
2864 if (usePrintfCond) {
2866 sv::MacroRefExprOp::create(builder, cond.getType(),
"PRINTF_COND_");
2867 ifCond = builder.createOrFold<
comb::AndOp>(ifCond, cond,
true);
2870 addIfProceduralBlock(ifCond, [&]() {
2874 if (fileDescriptor.isDefaultFd()) {
2876 fd = hw::ConstantOp::create(builder, APInt(32, 0x80000002));
2879 auto fdOrError = callFileDescriptorLib(fileDescriptor);
2880 if (llvm::failed(fdOrError)) {
2886 failed = llvm::failed(fn(fd));
2890 return failure(failed);
2894FIRRTLLowering::callFileDescriptorLib(
const FileDescriptorInfo &info) {
2895 circuitState.usedFileDescriptorLib =
true;
2896 circuitState.addFragment(theModule,
"CIRCT_LIB_LOGGING_FRAGMENT");
2899 if (
info.isSubstitutionRequired()) {
2900 SmallVector<Value> fileNameOperands;
2901 if (failed(loweredFmtOperands(
info.getSubstitutions(), fileNameOperands)))
2904 fileName = sv::SFormatFOp::create(builder,
info.getOutputFileFormat(),
2909 fileName = sv::ConstantStrOp::create(builder,
info.getOutputFileFormat())
2913 return sv::FuncCallProceduralOp::create(
2914 builder, mlir::TypeRange{builder.getIntegerType(32)},
2915 builder.getStringAttr(
"__circt_lib_logging::FileDescriptor::get"),
2916 ValueRange{fileName})
2926LogicalResult FIRRTLLowering::setLowering(Value orig, Value result) {
2927 if (
auto origType = dyn_cast<FIRRTLType>(orig.getType())) {
2928 assert((!result || !type_isa<FIRRTLType>(result.getType())) &&
2929 "Lowering didn't turn a FIRRTL value into a non-FIRRTL value");
2933 auto srcWidth = baseType.getPassiveType().getBitWidthOrSentinel();
2936 if (srcWidth != -1) {
2938 assert((srcWidth != 0) &&
2939 "Lowering produced value for zero width source");
2941 assert((srcWidth == 0) &&
2942 "Lowering produced null value but source wasn't zero width");
2946 assert(result &&
"Lowering of foreign type produced null value");
2949 auto &slot = valueMapping[orig];
2950 assert(!slot &&
"value lowered multiple times");
2957LogicalResult FIRRTLLowering::setPossiblyFoldedLowering(Value orig,
2961 if (
auto cst = dyn_cast_or_null<hw::ConstantOp>(result.getDefiningOp())) {
2962 auto &entry = hwConstantMap[cst.getValueAttr()];
2973 cst->moveBefore(&theModule.getBodyBlock()->front());
2977 return setLowering(orig, result);
2982template <
typename ResultOpType,
typename... CtorArgTypes>
2983LogicalResult FIRRTLLowering::setLoweringTo(Operation *orig,
2984 CtorArgTypes... args) {
2985 auto result = builder.createOrFold<ResultOpType>(args...);
2986 if (
auto *op = result.getDefiningOp())
2988 return setPossiblyFoldedLowering(orig->getResult(0), result);
2995template <
typename ResultOpType,
typename... CtorArgTypes>
2996LogicalResult FIRRTLLowering::setLoweringToLTL(Operation *orig,
2997 CtorArgTypes... args) {
2998 auto result = builder.createOrFold<ResultOpType>(args...);
2999 if (
auto *op = result.getDefiningOp())
3000 ltlOpFixupWorklist.insert(op);
3001 return setPossiblyFoldedLowering(orig->getResult(0), result);
3010Backedge FIRRTLLowering::createBackedge(Location loc, Type type) {
3011 auto backedge = backedgeBuilder.
get(type, loc);
3012 backedges.insert({backedge, backedge});
3020Backedge FIRRTLLowering::createBackedge(Value orig, Type type) {
3021 auto backedge = createBackedge(orig.getLoc(), type);
3022 (void)setLowering(orig, backedge);
3028bool FIRRTLLowering::updateIfBackedge(Value dest, Value src) {
3029 auto backedgeIt = backedges.find(dest);
3030 if (backedgeIt == backedges.end())
3032 backedgeIt->second = src;
3040void FIRRTLLowering::runWithInsertionPointAtEndOfBlock(
3041 const std::function<
void(
void)> &fn, Region ®ion) {
3045 auto oldIP = builder.saveInsertionPoint();
3047 builder.setInsertionPointToEnd(®ion.front());
3049 builder.restoreInsertionPoint(oldIP);
3053Value FIRRTLLowering::getReadValue(Value v) {
3054 Value result = readInOutCreated.lookup(v);
3060 auto oldIP = builder.saveInsertionPoint();
3061 if (
auto *vOp = v.getDefiningOp()) {
3062 builder.setInsertionPointAfter(vOp);
3066 builder.setInsertionPoint(&theModule.getBodyBlock()->front());
3071 if (
auto arrayIndexInout = v.getDefiningOp<sv::ArrayIndexInOutOp>()) {
3072 result = getReadValue(arrayIndexInout.getInput());
3074 arrayIndexInout.getIndex());
3079 builder.restoreInsertionPoint(oldIP);
3080 readInOutCreated.insert({v, result});
3084Value FIRRTLLowering::getNonClockValue(Value v) {
3085 auto it = fromClockMapping.try_emplace(v, Value{});
3087 ImplicitLocOpBuilder builder(v.getLoc(), v.getContext());
3088 builder.setInsertionPointAfterValue(v);
3089 it.first->second = seq::FromClockOp::create(builder, v);
3091 return it.first->second;
3094void FIRRTLLowering::addToAlwaysBlock(
3095 sv::EventControl clockEdge, Value clock, sv::ResetType resetStyle,
3096 sv::EventControl resetEdge, Value reset,
3097 const std::function<
void(
void)> &body,
3098 const std::function<
void(
void)> &resetBody) {
3099 AlwaysKeyType key{builder.getBlock(), clockEdge, clock,
3100 resetStyle, resetEdge, reset};
3101 sv::AlwaysOp alwaysOp;
3102 sv::IfOp insideIfOp;
3103 std::tie(alwaysOp, insideIfOp) = alwaysBlocks.lookup(key);
3107 assert(resetStyle != sv::ResetType::NoReset);
3120 auto createIfOp = [&]() {
3123 insideIfOp = sv::IfOp::create(
3124 builder, reset, [] {}, [] {});
3126 if (resetStyle == sv::ResetType::AsyncReset) {
3127 sv::EventControl events[] = {clockEdge, resetEdge};
3128 Value clocks[] = {clock, reset};
3130 alwaysOp = sv::AlwaysOp::create(builder, events, clocks, [&]() {
3131 if (resetEdge == sv::EventControl::AtNegEdge)
3132 llvm_unreachable(
"negative edge for reset is not expected");
3136 alwaysOp = sv::AlwaysOp::create(builder, clockEdge, clock, createIfOp);
3140 alwaysOp = sv::AlwaysOp::create(builder, clockEdge, clock);
3141 insideIfOp =
nullptr;
3143 alwaysBlocks[key] = {alwaysOp, insideIfOp};
3147 assert(insideIfOp &&
"reset body must be initialized before");
3148 runWithInsertionPointAtEndOfBlock(resetBody, insideIfOp.getThenRegion());
3149 runWithInsertionPointAtEndOfBlock(body, insideIfOp.getElseRegion());
3151 runWithInsertionPointAtEndOfBlock(body, alwaysOp.getBody());
3157 alwaysOp->moveBefore(builder.getInsertionBlock(),
3158 builder.getInsertionPoint());
3161LogicalResult FIRRTLLowering::emitGuards(Location loc,
3162 ArrayRef<Attribute> guards,
3163 std::function<
void(
void)>
emit) {
3164 if (guards.empty()) {
3168 auto guard = dyn_cast<StringAttr>(guards[0]);
3170 return mlir::emitError(loc,
3171 "elements in `guards` array must be `StringAttr`");
3174 circuitState.addMacroDecl(builder.getStringAttr(guard.getValue()));
3175 LogicalResult result = LogicalResult::failure();
3176 addToIfDefBlock(guard.getValue(), [&]() {
3177 result = emitGuards(loc, guards.drop_front(), emit);
3182void FIRRTLLowering::addToIfDefBlock(StringRef cond,
3183 std::function<
void(
void)> thenCtor,
3184 std::function<
void(
void)> elseCtor) {
3185 auto condAttr = builder.getStringAttr(cond);
3186 auto op = ifdefBlocks.lookup({builder.getBlock(), condAttr});
3188 runWithInsertionPointAtEndOfBlock(thenCtor, op.getThenRegion());
3189 runWithInsertionPointAtEndOfBlock(elseCtor, op.getElseRegion());
3194 op->moveBefore(builder.getInsertionBlock(), builder.getInsertionPoint());
3196 ifdefBlocks[{builder.getBlock(), condAttr}] =
3197 sv::IfDefOp::create(builder, condAttr, thenCtor, elseCtor);
3201void FIRRTLLowering::addToInitialBlock(std::function<
void(
void)> body) {
3202 auto op = initialBlocks.lookup(builder.getBlock());
3204 runWithInsertionPointAtEndOfBlock(body, op.getBody());
3209 op->moveBefore(builder.getInsertionBlock(), builder.getInsertionPoint());
3211 initialBlocks[builder.getBlock()] = sv::InitialOp::create(builder, body);
3215void FIRRTLLowering::addIfProceduralBlock(Value cond,
3216 std::function<
void(
void)> thenCtor,
3217 std::function<
void(
void)> elseCtor) {
3220 auto insertIt = builder.getInsertionPoint();
3221 if (insertIt != builder.getBlock()->begin())
3222 if (
auto ifOp = dyn_cast<sv::IfOp>(*--insertIt)) {
3223 if (ifOp.getCond() == cond) {
3224 runWithInsertionPointAtEndOfBlock(thenCtor, ifOp.getThenRegion());
3225 runWithInsertionPointAtEndOfBlock(elseCtor, ifOp.getElseRegion());
3230 sv::IfOp::create(builder, cond, thenCtor, elseCtor);
3242FIRRTLLowering::UnloweredOpResult
3243FIRRTLLowering::handleUnloweredOp(Operation *op) {
3245 if (!op->getRegions().empty() &&
3246 isa_and_nonnull<FIRRTLDialect>(op->getDialect())) {
3247 op->emitOpError(
"must explicitly handle its regions");
3248 return LoweringFailure;
3255 if (!isa_and_nonnull<FIRRTLDialect>(op->getDialect())) {
3257 for (
auto ®ion : op->getRegions())
3258 addToWorklist(region);
3259 for (
auto &operand : op->getOpOperands())
3260 if (auto lowered = getPossiblyInoutLoweredValue(operand.
get()))
3261 operand.set(lowered);
3262 for (
auto result : op->getResults())
3263 (void)setLowering(result, result);
3264 return AlreadyLowered;
3276 if (op->getNumResults() == 1) {
3277 auto resultType = op->getResult(0).getType();
3278 if (type_isa<FIRRTLBaseType>(resultType) &&
3280 (
isExpression(op) || isa<mlir::UnrealizedConversionCastOp>(op))) {
3282 (void)setLowering(op->getResult(0), Value());
3286 op->emitOpError(
"LowerToHW couldn't handle this operation");
3287 return LoweringFailure;
3290LogicalResult FIRRTLLowering::visitExpr(ConstantOp op) {
3293 return setLowering(op, Value());
3295 return setLowering(op, getOrCreateIntConstant(op.getValue()));
3298LogicalResult FIRRTLLowering::visitExpr(SpecialConstantOp op) {
3300 if (isa<ClockType>(op.getType())) {
3301 cst = getOrCreateClockConstant(op.getValue() ? seq::ClockConst::High
3302 :
seq::ClockConst::Low);
3304 cst = getOrCreateIntConstant(APInt( 1, op.getValue()));
3306 return setLowering(op, cst);
3309FailureOr<Value> FIRRTLLowering::lowerSubindex(SubindexOp op, Value input) {
3310 auto iIdx = getOrCreateIntConstant(
3312 firrtl::type_cast<FVectorType>(op.getInput().getType())
3319 if (isa<sv::InOutType>(input.getType()))
3320 result = builder.createOrFold<sv::ArrayIndexInOutOp>(input, iIdx);
3323 if (
auto *definingOp = result.getDefiningOp())
3328FailureOr<Value> FIRRTLLowering::lowerSubaccess(SubaccessOp op, Value input) {
3329 Value valueIdx = getLoweredAndExtOrTruncValue(
3331 UIntType::get(op->getContext(),
3333 firrtl::type_cast<FVectorType>(op.getInput().getType())
3334 .getNumElements())));
3336 op->emitError() <<
"input lowering failed";
3343 if (isa<sv::InOutType>(input.getType()))
3344 result = builder.createOrFold<sv::ArrayIndexInOutOp>(input, valueIdx);
3346 result = createArrayIndexing(input, valueIdx);
3347 if (
auto *definingOp = result.getDefiningOp())
3352FailureOr<Value> FIRRTLLowering::lowerSubfield(SubfieldOp op, Value input) {
3353 auto resultType =
lowerType(op->getResult(0).getType());
3354 if (!resultType || !input) {
3355 op->emitError() <<
"subfield type lowering failed";
3361 auto field = firrtl::type_cast<BundleType>(op.getInput().getType())
3362 .getElementName(op.getFieldIndex());
3364 if (isa<sv::InOutType>(input.getType()))
3365 result = builder.createOrFold<sv::StructFieldInOutOp>(input, field);
3368 if (
auto *definingOp = result.getDefiningOp())
3373LogicalResult FIRRTLLowering::visitExpr(SubindexOp op) {
3375 return setLowering(op, Value());
3377 auto input = getPossiblyInoutLoweredValue(op.getInput());
3379 return op.emitError() <<
"input lowering failed";
3381 auto result = lowerSubindex(op, input);
3384 return setLowering(op, *result);
3387LogicalResult FIRRTLLowering::visitExpr(SubaccessOp op) {
3389 return setLowering(op, Value());
3391 auto input = getPossiblyInoutLoweredValue(op.getInput());
3393 return op.emitError() <<
"input lowering failed";
3395 auto result = lowerSubaccess(op, input);
3398 return setLowering(op, *result);
3401LogicalResult FIRRTLLowering::visitExpr(SubfieldOp op) {
3404 if (getLoweredValue(op) || !op.getInput())
3408 return setLowering(op, Value());
3410 auto input = getPossiblyInoutLoweredValue(op.getInput());
3412 return op.emitError() <<
"input lowering failed";
3414 auto result = lowerSubfield(op, input);
3417 return setLowering(op, *result);
3420LogicalResult FIRRTLLowering::visitExpr(VectorCreateOp op) {
3421 auto resultType =
lowerType(op.getResult().getType());
3422 SmallVector<Value> operands;
3424 for (
auto oper :
llvm::reverse(op.getOperands())) {
3425 auto val = getLoweredValue(oper);
3428 operands.push_back(val);
3430 return setLoweringTo<hw::ArrayCreateOp>(op, resultType, operands);
3433LogicalResult FIRRTLLowering::visitExpr(BundleCreateOp op) {
3434 auto resultType =
lowerType(op.getResult().getType());
3435 SmallVector<Value> operands;
3436 for (
auto oper : op.getOperands()) {
3437 auto val = getLoweredValue(oper);
3440 operands.push_back(val);
3442 return setLoweringTo<hw::StructCreateOp>(op, resultType, operands);
3445LogicalResult FIRRTLLowering::visitExpr(FEnumCreateOp op) {
3448 return setLowering(op, Value());
3450 auto input = getLoweredValue(op.getInput());
3451 auto tagName = op.getFieldNameAttr();
3452 auto oldType = op.getType().base();
3454 auto element = *oldType.getElement(op.getFieldNameAttr());
3456 if (
auto structType = dyn_cast<hw::StructType>(newType)) {
3457 auto tagType = structType.getFieldType(
"tag");
3458 auto tagValue = IntegerAttr::get(tagType, element.value.getValue());
3459 auto tag = sv::LocalParamOp::create(builder, op.getLoc(), tagType, tagValue,
3461 auto bodyType = structType.getFieldType(
"body");
3462 auto body = hw::UnionCreateOp::create(builder, bodyType, tagName, input);
3463 SmallVector<Value> operands = {tag.getResult(), body.getResult()};
3464 return setLoweringTo<hw::StructCreateOp>(op, structType, operands);
3466 auto tagValue = IntegerAttr::get(newType, element.value.getValue());
3467 return setLoweringTo<sv::LocalParamOp>(op, newType, tagValue, tagName);
3470LogicalResult FIRRTLLowering::visitExpr(AggregateConstantOp op) {
3471 auto resultType =
lowerType(op.getResult().getType());
3473 getOrCreateAggregateConstantAttribute(op.getFieldsAttr(), resultType);
3475 return setLoweringTo<hw::AggregateConstantOp>(op, resultType,
3476 cast<ArrayAttr>(attr));
3479LogicalResult FIRRTLLowering::visitExpr(IsTagOp op) {
3481 auto tagName = op.getFieldNameAttr();
3482 auto lhs = getLoweredValue(op.getInput());
3483 if (isa<hw::StructType>(lhs.getType()))
3486 auto index = op.getFieldIndex();
3487 auto enumType = op.getInput().getType().base();
3488 auto tagValue = enumType.getElementValueAttr(index);
3489 auto tagValueType = IntegerType::get(op.getContext(), enumType.getTagWidth());
3490 auto loweredTagValue = IntegerAttr::get(tagValueType, tagValue.getValue());
3491 auto rhs = sv::LocalParamOp::create(builder, op.getLoc(), tagValueType,
3492 loweredTagValue, tagName);
3494 Type resultType = builder.getIntegerType(1);
3495 return setLoweringTo<comb::ICmpOp>(op, resultType, ICmpPredicate::eq, lhs,
3499LogicalResult FIRRTLLowering::visitExpr(SubtagOp op) {
3502 return setLowering(op, Value());
3504 auto tagName = op.getFieldNameAttr();
3505 auto input = getLoweredValue(op.getInput());
3507 return setLoweringTo<hw::UnionExtractOp>(op, field, tagName);
3510LogicalResult FIRRTLLowering::visitExpr(TagExtractOp op) {
3513 return setLowering(op, Value());
3515 auto input = getLoweredValue(op.getInput());
3521 if (isa<hw::StructType>(input.getType())) {
3522 return setLoweringTo<hw::StructExtractOp>(op, input,
"tag");
3527 return setLowering(op, input);
3534LogicalResult FIRRTLLowering::visitDecl(WireOp op) {
3535 auto origResultType = op.getResult().getType();
3539 if (!type_isa<FIRRTLType>(origResultType)) {
3540 createBackedge(op.getResult(), origResultType);
3544 auto resultType =
lowerType(origResultType);
3548 if (resultType.isInteger(0)) {
3549 if (op.getInnerSym())
3550 return op.emitError(
"zero width wire is referenced by name [")
3551 << *op.getInnerSym() <<
"] (e.g. in an XMR) but must be removed";
3552 return setLowering(op.getResult(), Value());
3556 auto innerSym = lowerInnerSymbol(op);
3557 auto name = op.getNameAttr();
3560 auto wire = hw::WireOp::create(
3561 builder, op.getLoc(), getOrCreateZConstant(resultType), name, innerSym);
3563 if (
auto svAttrs = sv::getSVAttributes(op))
3564 sv::setSVAttributes(wire, svAttrs);
3566 return setLowering(op.getResult(), wire);
3569LogicalResult FIRRTLLowering::visitDecl(VerbatimWireOp op) {
3570 auto resultTy =
lowerType(op.getType());
3573 resultTy = sv::InOutType::get(op.getContext(), resultTy);
3575 SmallVector<Value, 4> operands;
3576 operands.reserve(op.getSubstitutions().size());
3577 for (
auto operand : op.getSubstitutions()) {
3578 auto lowered = getLoweredValue(operand);
3581 operands.push_back(lowered);
3584 ArrayAttr symbols = op.getSymbolsAttr();
3586 symbols = ArrayAttr::get(op.getContext(), {});
3588 return setLoweringTo<sv::VerbatimExprSEOp>(op, resultTy, op.getTextAttr(),
3592LogicalResult FIRRTLLowering::visitDecl(NodeOp op) {
3593 auto operand = getLoweredValue(op.getInput());
3595 return handleZeroBit(op.getInput(), [&]() -> LogicalResult {
3596 if (op.getInnerSym())
3597 return op.emitError(
"zero width node is referenced by name [")
3598 << *op.getInnerSym()
3599 <<
"] (e.g. in an XMR) but must be "
3601 return setLowering(op.getResult(), Value());
3607 auto name = op.getNameAttr();
3608 auto innerSym = lowerInnerSymbol(op);
3611 operand = hw::WireOp::create(builder, operand, name, innerSym);
3614 if (
auto svAttrs = sv::getSVAttributes(op)) {
3616 operand = hw::WireOp::create(builder, operand, name);
3617 sv::setSVAttributes(operand.getDefiningOp(), svAttrs);
3620 return setLowering(op.getResult(), operand);
3623LogicalResult FIRRTLLowering::visitDecl(RegOp op) {
3624 auto resultType =
lowerType(op.getResult().getType());
3627 if (resultType.isInteger(0))
3628 return setLowering(op.getResult(), Value());
3630 Value clockVal = getLoweredValue(op.getClockVal());
3635 auto innerSym = lowerInnerSymbol(op);
3636 Backedge inputEdge = backedgeBuilder.
get(resultType);
3637 auto reg = seq::FirRegOp::create(builder, inputEdge, clockVal,
3638 op.getNameAttr(), innerSym);
3641 if (
auto randomRegister = op->getAttr(
"firrtl.random_init_register"))
3642 reg->setAttr(
"firrtl.random_init_register", randomRegister);
3643 if (
auto randomStart = op->getAttr(
"firrtl.random_init_start"))
3644 reg->setAttr(
"firrtl.random_init_start", randomStart);
3645 if (
auto randomEnd = op->getAttr(
"firrtl.random_init_end"))
3646 reg->setAttr(
"firrtl.random_init_end", randomEnd);
3649 if (
auto svAttrs = sv::getSVAttributes(op))
3650 sv::setSVAttributes(reg, svAttrs);
3653 (void)setLowering(op.getResult(),
reg);
3657LogicalResult FIRRTLLowering::visitDecl(RegResetOp op) {
3658 auto resultType =
lowerType(op.getResult().getType());
3661 if (resultType.isInteger(0))
3662 return setLowering(op.getResult(), Value());
3664 Value clockVal = getLoweredValue(op.getClockVal());
3665 Value resetSignal = getLoweredValue(op.getResetSignal());
3667 Value resetValue = getLoweredAndExtOrTruncValue(
3668 op.getResetValue(), type_cast<FIRRTLBaseType>(op.getResult().getType()));
3670 if (!clockVal || !resetSignal || !resetValue)
3674 auto innerSym = lowerInnerSymbol(op);
3675 bool isAsync = type_isa<AsyncResetType>(op.getResetSignal().getType());
3676 Backedge inputEdge = backedgeBuilder.
get(resultType);
3678 seq::FirRegOp::create(builder, inputEdge, clockVal, op.getNameAttr(),
3679 resetSignal, resetValue, innerSym, isAsync);
3682 if (
auto randomRegister = op->getAttr(
"firrtl.random_init_register"))
3683 reg->setAttr(
"firrtl.random_init_register", randomRegister);
3684 if (
auto randomStart = op->getAttr(
"firrtl.random_init_start"))
3685 reg->setAttr(
"firrtl.random_init_start", randomStart);
3686 if (
auto randomEnd = op->getAttr(
"firrtl.random_init_end"))
3687 reg->setAttr(
"firrtl.random_init_end", randomEnd);
3690 if (
auto svAttrs = sv::getSVAttributes(op))
3691 sv::setSVAttributes(reg, svAttrs);
3694 (void)setLowering(op.getResult(),
reg);
3699LogicalResult FIRRTLLowering::visitDecl(MemOp op) {
3702 if (type_isa<BundleType>(op.getDataType()))
3703 return op.emitOpError(
3704 "should have already been lowered from a ground type to an aggregate "
3705 "type using the LowerTypes pass. Use "
3706 "'firtool --lower-types' or 'circt-opt "
3707 "--pass-pipeline='firrtl.circuit(firrtl-lower-types)' "
3713 auto memType = seq::FirMemType::get(
3716 : std::optional<uint32_t>());
3718 seq::FirMemInitAttr memInit;
3719 if (
auto init = op.getInitAttr())
3720 memInit = seq::FirMemInitAttr::get(init.getContext(), init.getFilename(),
3721 init.getIsBinary(), init.getIsInline());
3723 auto memDecl = seq::FirMemOp::create(
3726 op.getInnerSymAttr(), memInit, op.getPrefixAttr(), Attribute{});
3729 if (
auto file = parent->getAttrOfType<hw::OutputFileAttr>(
"output_file")) {
3731 if (!file.isDirectory())
3732 dir = hw::OutputFileAttr::getAsDirectory(builder.getContext(),
3733 file.getDirectory());
3734 memDecl.setOutputFileAttr(dir);
3740 for (
size_t i = 0, e = op.getNumResults(); i != e; ++i) {
3742 auto addOutput = [&](StringRef field,
size_t width, Value value) {
3745 (void)setLowering(a, value);
3751 auto addInput = [&](StringRef field, Value backedge) {
3753 if (cast<FIRRTLBaseType>(a.getType())
3755 .getBitWidthOrSentinel() > 0)
3756 (void)setLowering(a, backedge);
3762 auto addInputPort = [&](StringRef field,
size_t width) -> Value {
3766 Value backedge, portValue;
3768 portValue = getOrCreateXConstant(1);
3770 auto portType = IntegerType::get(op.getContext(), width);
3771 backedge = portValue = createBackedge(builder.getLoc(), portType);
3773 addInput(field, backedge);
3777 auto addClock = [&](StringRef field) -> Value {
3778 Type clockTy = seq::ClockType::get(op.getContext());
3779 Value portValue = createBackedge(builder.getLoc(), clockTy);
3780 addInput(field, portValue);
3784 auto memportKind = op.getPortKind(i);
3785 if (memportKind == MemOp::PortKind::Read) {
3786 auto addr = addInputPort(
"addr", op.getAddrBits());
3787 auto en = addInputPort(
"en", 1);
3788 auto clk = addClock(
"clk");
3789 auto data = seq::FirMemReadOp::create(builder, memDecl,
addr,
clk,
en);
3791 }
else if (memportKind == MemOp::PortKind::ReadWrite) {
3792 auto addr = addInputPort(
"addr", op.getAddrBits());
3793 auto en = addInputPort(
"en", 1);
3794 auto clk = addClock(
"clk");
3797 auto mode = addInputPort(
"wmode", 1);
3799 mode = builder.createOrFold<
comb::AndOp>(mode, addInputPort(
"wmask", 1),
3806 auto rdata = seq::FirMemReadWriteOp::create(builder, memDecl,
addr,
clk,
3810 auto addr = addInputPort(
"addr", op.getAddrBits());
3813 auto en = addInputPort(
"en", 1);
3817 auto clk = addClock(
"clk");
3830LogicalResult FIRRTLLowering::visitDecl(InstanceOp oldInstance) {
3831 Operation *oldModule =
3832 oldInstance.getReferencedModule(circuitState.getInstanceGraph());
3834 auto *newModule = circuitState.getNewModule(oldModule);
3836 oldInstance->emitOpError(
"could not find module [")
3837 << oldInstance.getModuleName() <<
"] referenced by instance";
3843 ArrayAttr parameters;
3844 if (
auto oldExtModule = dyn_cast<FExtModuleOp>(oldModule))
3849 SmallVector<PortInfo, 8> portInfo = cast<FModuleLike>(oldModule).getPorts();
3854 for (
unsigned portIdx = 0, e = portInfo.size(); portIdx != e; ++portIdx)
3855 portIndicesByName[portInfo[portIdx].name] = portIdx;
3859 SmallVector<Value, 8> operands;
3860 for (
size_t portIndex = 0, e = portInfo.size(); portIndex != e; ++portIndex) {
3861 auto &port = portInfo[portIndex];
3864 oldInstance->emitOpError(
"could not lower type of port ") << port.name;
3869 if (portType.isInteger(0))
3873 if (port.isOutput())
3876 auto portResult = oldInstance.getResult(portIndex);
3877 assert(portResult &&
"invalid IR, couldn't find port");
3881 if (port.isInput()) {
3882 operands.push_back(createBackedge(portResult, portType));
3888 if (type_isa<AnalogType>(portResult.getType()) && portResult.hasOneUse()) {
3889 if (
auto attach = dyn_cast<AttachOp>(*portResult.getUsers().begin())) {
3891 auto loweredResult = getPossiblyInoutLoweredValue(source);
3892 operands.push_back(loweredResult);
3893 (void)setLowering(portResult, loweredResult);
3902 "." + port.getName().str() +
".wire");
3906 (void)setLowering(portResult, wire);
3908 operands.push_back(wire);
3915 auto innerSym = oldInstance.getInnerSymAttr();
3916 if (oldInstance.getLowerToBind()) {
3919 oldInstance.getContext(), oldInstance.getInnerSymAttr(), 0,
3922 auto bindOp = sv::BindOp::create(builder, theModule.getNameAttr(),
3923 innerSym.getSymName());
3926 if (
auto outputFile = oldInstance->getAttr(
"output_file"))
3927 bindOp->setAttr(
"output_file", outputFile);
3930 circuitState.addBind(bindOp);
3935 hw::InstanceOp::create(builder, newModule, oldInstance.getNameAttr(),
3936 operands, parameters, innerSym);
3938 if (oldInstance.getLowerToBind() || oldInstance.getDoNotPrint())
3939 newInstance.setDoNotPrintAttr(builder.getUnitAttr());
3941 if (newInstance.getInnerSymAttr())
3942 if (
auto forceName = circuitState.instanceForceNames.lookup(
3943 {newInstance->getParentOfType<hw::HWModuleOp>().getNameAttr(),
3944 newInstance.getInnerNameAttr()}))
3945 newInstance->setAttr(
"hw.verilogName", forceName);
3949 unsigned resultNo = 0;
3950 for (
size_t portIndex = 0, e = portInfo.size(); portIndex != e; ++portIndex) {
3951 auto &port = portInfo[portIndex];
3955 Value resultVal = newInstance.getResult(resultNo);
3957 auto oldPortResult = oldInstance.getResult(portIndex);
3958 (void)setLowering(oldPortResult, resultVal);
3964LogicalResult FIRRTLLowering::visitDecl(ContractOp oldOp) {
3965 SmallVector<Value> inputs;
3966 SmallVector<Type> types;
3967 for (
auto input : oldOp.getInputs()) {
3968 auto lowered = getLoweredValue(input);
3971 inputs.push_back(lowered);
3972 types.push_back(lowered.getType());
3975 auto newOp = verif::ContractOp::create(builder, types, inputs);
3976 newOp->setDiscardableAttrs(oldOp->getDiscardableAttrDictionary());
3977 auto &body = newOp.getBody().emplaceBlock();
3979 for (
auto [newResult, oldResult, oldArg] :
3980 llvm::zip(newOp.getResults(), oldOp.getResults(),
3981 oldOp.getBody().getArguments())) {
3982 if (failed(setLowering(oldResult, newResult)))
3984 if (failed(setLowering(oldArg, newResult)))
3988 body.getOperations().splice(body.end(),
3989 oldOp.getBody().front().getOperations());
3990 addToWorklist(body);
4000LogicalResult FIRRTLLowering::lowerNoopCast(Operation *op) {
4001 auto operand = getPossiblyInoutLoweredValue(op->getOperand(0));
4006 return setLowering(op->getResult(0), operand);
4009LogicalResult FIRRTLLowering::visitExpr(AsSIntPrimOp op) {
4010 if (isa<ClockType>(op.getInput().getType()))
4011 return setLowering(op->getResult(0),
4012 getLoweredNonClockValue(op.getInput()));
4013 return lowerNoopCast(op);
4016LogicalResult FIRRTLLowering::visitExpr(AsUIntPrimOp op) {
4017 if (isa<ClockType>(op.getInput().getType()))
4018 return setLowering(op->getResult(0),
4019 getLoweredNonClockValue(op.getInput()));
4020 return lowerNoopCast(op);
4023LogicalResult FIRRTLLowering::visitExpr(AsClockPrimOp op) {
4024 return setLoweringTo<seq::ToClockOp>(op, getLoweredValue(op.getInput()));
4027LogicalResult FIRRTLLowering::visitUnrealizedConversionCast(
4028 mlir::UnrealizedConversionCastOp op) {
4030 if (op.getNumOperands() != 1 || op.getNumResults() != 1)
4033 auto operand = op.getOperand(0);
4034 auto result = op.getResult(0);
4037 if (type_isa<FIRRTLType>(operand.getType()) &&
4038 type_isa<FIRRTLType>(result.getType()))
4039 return lowerNoopCast(op);
4043 if (!type_isa<FIRRTLType>(operand.getType())) {
4044 if (type_isa<FIRRTLType>(result.getType()))
4045 return setLowering(result, getPossiblyInoutLoweredValue(operand));
4051 auto loweredResult = getLoweredValue(operand);
4052 if (!loweredResult) {
4055 if (operand.getType().isSignlessInteger(0)) {
4056 return setLowering(result, Value());
4063 result.replaceAllUsesWith(loweredResult);
4067LogicalResult FIRRTLLowering::visitExpr(HWStructCastOp op) {
4070 if (
auto opStructType = dyn_cast<hw::StructType>(op.getOperand().getType()))
4071 return setLowering(op, op.getOperand());
4075 auto result = getLoweredValue(op.getOperand());
4081 op.replaceAllUsesWith(result);
4085LogicalResult FIRRTLLowering::visitExpr(BitCastOp op) {
4086 auto operand = getLoweredValue(op.getOperand());
4089 auto resultType =
lowerType(op.getType());
4093 return setLoweringTo<hw::BitcastOp>(op, resultType, operand);
4096LogicalResult FIRRTLLowering::visitExpr(CvtPrimOp op) {
4097 auto operand = getLoweredValue(op.getOperand());
4101 if (type_cast<IntType>(op.getOperand().getType()).isUnsigned())
4102 return setLowering(op, getOrCreateIntConstant(1, 0));
4104 return setLowering(op, Value());
4109 if (type_cast<IntType>(op.getOperand().getType()).isSigned())
4110 return setLowering(op, operand);
4113 auto zero = getOrCreateIntConstant(1, 0);
4114 return setLoweringTo<comb::ConcatOp>(op, zero, operand);
4117LogicalResult FIRRTLLowering::visitExpr(NotPrimOp op) {
4118 auto operand = getLoweredValue(op.getInput());
4122 auto allOnes = getOrCreateIntConstant(
4123 APInt::getAllOnes(operand.getType().getIntOrFloatBitWidth()));
4124 return setLoweringTo<comb::XorOp>(op, operand, allOnes,
true);
4127LogicalResult FIRRTLLowering::visitExpr(NegPrimOp op) {
4130 auto operand = getLoweredAndExtendedValue(op.getInput(), op.getType());
4134 auto resultType =
lowerType(op.getType());
4136 auto zero = getOrCreateIntConstant(resultType.getIntOrFloatBitWidth(), 0);
4137 return setLoweringTo<comb::SubOp>(op, zero, operand,
true);
4141LogicalResult FIRRTLLowering::visitExpr(PadPrimOp op) {
4142 auto operand = getLoweredAndExtendedValue(op.getInput(), op.getType());
4145 return setLowering(op, operand);
4148LogicalResult FIRRTLLowering::visitExpr(XorRPrimOp op) {
4149 auto operand = getLoweredValue(op.getInput());
4152 return setLowering(op, getOrCreateIntConstant(1, 0));
4157 return setLoweringTo<comb::ParityOp>(op, builder.getIntegerType(1), operand,
4161LogicalResult FIRRTLLowering::visitExpr(AndRPrimOp op) {
4162 auto operand = getLoweredValue(op.getInput());
4165 return setLowering(op, getOrCreateIntConstant(1, 1));
4170 return setLoweringTo<comb::ICmpOp>(
4171 op, ICmpPredicate::eq, operand,
4172 getOrCreateIntConstant(
4173 APInt::getAllOnes(operand.getType().getIntOrFloatBitWidth())),
4177LogicalResult FIRRTLLowering::visitExpr(OrRPrimOp op) {
4178 auto operand = getLoweredValue(op.getInput());
4181 return setLowering(op, getOrCreateIntConstant(1, 0));
4187 return setLoweringTo<comb::ICmpOp>(
4188 op, ICmpPredicate::ne, operand,
4189 getOrCreateIntConstant(operand.getType().getIntOrFloatBitWidth(), 0),
4197template <
typename ResultOpType>
4198LogicalResult FIRRTLLowering::lowerBinOpToVariadic(Operation *op) {
4199 auto resultType = op->getResult(0).getType();
4200 auto lhs = getLoweredAndExtendedValue(op->getOperand(0), resultType);
4201 auto rhs = getLoweredAndExtendedValue(op->getOperand(1), resultType);
4205 return setLoweringTo<ResultOpType>(op, lhs, rhs,
true);
4211template <
typename ResultOpType>
4212LogicalResult FIRRTLLowering::lowerElementwiseLogicalOp(Operation *op) {
4213 auto resultType = op->getResult(0).getType();
4214 auto lhs = getLoweredAndExtendedValue(op->getOperand(0), resultType);
4215 auto rhs = getLoweredAndExtendedValue(op->getOperand(1), resultType);
4226 auto intType = builder.getIntegerType(*bitwidth);
4227 auto retType = lhs.getType();
4230 auto result = builder.createOrFold<ResultOpType>(lhs, rhs,
true);
4231 return setLoweringTo<hw::BitcastOp>(op, retType, result);
4236template <
typename ResultUn
signedOpType,
typename ResultSignedOpType>
4237LogicalResult FIRRTLLowering::lowerBinOp(Operation *op) {
4239 auto resultType = op->getResult(0).getType();
4240 auto lhs = getLoweredAndExtendedValue(op->getOperand(0), resultType);
4241 auto rhs = getLoweredAndExtendedValue(op->getOperand(1), resultType);
4246 if (type_cast<IntType>(resultType).isSigned())
4247 return setLoweringTo<ResultSignedOpType>(op, lhs, rhs,
true);
4248 return setLoweringTo<ResultUnsignedOpType>(op, lhs, rhs,
true);
4253LogicalResult FIRRTLLowering::lowerCmpOp(Operation *op, ICmpPredicate signedOp,
4254 ICmpPredicate unsignedOp) {
4256 auto lhsIntType = type_cast<IntType>(op->getOperand(0).getType());
4257 auto rhsIntType = type_cast<IntType>(op->getOperand(1).getType());
4258 if (!lhsIntType.hasWidth() || !rhsIntType.hasWidth())
4262 if (cmpType.getWidth() == 0)
4263 cmpType = UIntType::get(builder.getContext(), 1);
4264 auto lhs = getLoweredAndExtendedValue(op->getOperand(0), cmpType);
4265 auto rhs = getLoweredAndExtendedValue(op->getOperand(1), cmpType);
4270 Type resultType = builder.getIntegerType(1);
4271 return setLoweringTo<comb::ICmpOp>(
4272 op, resultType, lhsIntType.isSigned() ? signedOp : unsignedOp, lhs, rhs,
4278template <
typename SignedOp,
typename Un
signedOp>
4279LogicalResult FIRRTLLowering::lowerDivLikeOp(Operation *op) {
4283 auto opType = type_cast<IntType>(op->getResult(0).getType());
4284 if (opType.getWidth() == 0)
4285 return setLowering(op->getResult(0), Value());
4289 auto lhs = getLoweredAndExtendedValue(op->getOperand(0), resultType);
4290 auto rhs = getLoweredAndExtendedValue(op->getOperand(1), resultType);
4295 if (opType.isSigned())
4296 result = builder.createOrFold<SignedOp>(lhs, rhs,
true);
4298 result = builder.createOrFold<UnsignedOp>(lhs, rhs,
true);
4300 if (
auto *definingOp = result.getDefiningOp())
4303 if (resultType == opType)
4304 return setLowering(op->getResult(0), result);
4305 return setLoweringTo<comb::ExtractOp>(op,
lowerType(opType), result, 0);
4308LogicalResult FIRRTLLowering::visitExpr(CatPrimOp op) {
4310 if (op.getInputs().empty())
4311 return setLowering(op, Value());
4313 SmallVector<Value> loweredOperands;
4316 for (
auto operand : op.getInputs()) {
4317 auto loweredOperand = getLoweredValue(operand);
4318 if (loweredOperand) {
4319 loweredOperands.push_back(loweredOperand);
4322 auto result =
handleZeroBit(operand, [&]() {
return success(); });
4330 if (loweredOperands.empty())
4331 return setLowering(op, Value());
4334 return setLoweringTo<comb::ConcatOp>(op, loweredOperands);
4341LogicalResult FIRRTLLowering::visitExpr(IsXIntrinsicOp op) {
4342 auto input = getLoweredNonClockValue(op.getArg());
4346 if (!isa<IntType>(input.getType())) {
4347 auto srcType = op.getArg().getType();
4349 assert(bitwidth &&
"Unknown width");
4350 auto intType = builder.getIntegerType(*bitwidth);
4351 input = builder.createOrFold<
hw::BitcastOp>(intType, input);
4354 return setLoweringTo<comb::ICmpOp>(
4355 op, ICmpPredicate::ceq, input,
4356 getOrCreateXConstant(input.getType().getIntOrFloatBitWidth()),
true);
4359LogicalResult FIRRTLLowering::visitStmt(FPGAProbeIntrinsicOp op) {
4360 auto operand = getLoweredValue(op.getInput());
4361 hw::WireOp::create(builder, operand);
4365LogicalResult FIRRTLLowering::visitExpr(PlusArgsTestIntrinsicOp op) {
4366 return setLoweringTo<sim::PlusArgsTestOp>(op, builder.getIntegerType(1),
4367 op.getFormatStringAttr());
4370LogicalResult FIRRTLLowering::visitExpr(PlusArgsValueIntrinsicOp op) {
4371 auto type =
lowerType(op.getResult().getType());
4375 auto valueOp = sim::PlusArgsValueOp::create(
4376 builder, builder.getIntegerType(1), type, op.getFormatStringAttr());
4377 if (failed(setLowering(op.getResult(), valueOp.getResult())))
4379 if (failed(setLowering(op.getFound(), valueOp.getFound())))
4384LogicalResult FIRRTLLowering::visitExpr(SizeOfIntrinsicOp op) {
4385 op.emitError(
"SizeOf should have been resolved.");
4389LogicalResult FIRRTLLowering::visitExpr(ClockGateIntrinsicOp op) {
4391 if (op.getTestEnable())
4392 testEnable = getLoweredValue(op.getTestEnable());
4393 return setLoweringTo<seq::ClockGateOp>(
4394 op, getLoweredValue(op.getInput()), getLoweredValue(op.getEnable()),
4395 testEnable, hw::InnerSymAttr{});
4398LogicalResult FIRRTLLowering::visitExpr(ClockInverterIntrinsicOp op) {
4399 auto operand = getLoweredValue(op.getInput());
4400 return setLoweringTo<seq::ClockInverterOp>(op, operand);
4403LogicalResult FIRRTLLowering::visitExpr(ClockDividerIntrinsicOp op) {
4404 auto operand = getLoweredValue(op.getInput());
4405 return setLoweringTo<seq::ClockDividerOp>(op, operand, op.getPow2());
4408LogicalResult FIRRTLLowering::visitExpr(LTLAndIntrinsicOp op) {
4409 return setLoweringToLTL<ltl::AndOp>(
4411 ValueRange{getLoweredValue(op.getLhs()), getLoweredValue(op.getRhs())});
4414LogicalResult FIRRTLLowering::visitExpr(LTLOrIntrinsicOp op) {
4415 return setLoweringToLTL<ltl::OrOp>(
4417 ValueRange{getLoweredValue(op.getLhs()), getLoweredValue(op.getRhs())});
4420LogicalResult FIRRTLLowering::visitExpr(LTLIntersectIntrinsicOp op) {
4421 return setLoweringToLTL<ltl::IntersectOp>(
4423 ValueRange{getLoweredValue(op.getLhs()), getLoweredValue(op.getRhs())});
4426LogicalResult FIRRTLLowering::visitExpr(LTLDelayIntrinsicOp op) {
4427 return setLoweringToLTL<ltl::DelayOp>(op, getLoweredValue(op.getInput()),
4428 op.getDelayAttr(), op.getLengthAttr());
4431LogicalResult FIRRTLLowering::visitExpr(LTLConcatIntrinsicOp op) {
4432 return setLoweringToLTL<ltl::ConcatOp>(
4434 ValueRange{getLoweredValue(op.getLhs()), getLoweredValue(op.getRhs())});
4437LogicalResult FIRRTLLowering::visitExpr(LTLRepeatIntrinsicOp op) {
4438 return setLoweringToLTL<ltl::RepeatOp>(op, getLoweredValue(op.getInput()),
4439 op.getBaseAttr(), op.getMoreAttr());
4442LogicalResult FIRRTLLowering::visitExpr(LTLGoToRepeatIntrinsicOp op) {
4443 return setLoweringToLTL<ltl::GoToRepeatOp>(
4444 op, getLoweredValue(op.getInput()), op.getBaseAttr(), op.getMoreAttr());
4447LogicalResult FIRRTLLowering::visitExpr(LTLNonConsecutiveRepeatIntrinsicOp op) {
4448 return setLoweringToLTL<ltl::NonConsecutiveRepeatOp>(
4449 op, getLoweredValue(op.getInput()), op.getBaseAttr(), op.getMoreAttr());
4452LogicalResult FIRRTLLowering::visitExpr(LTLNotIntrinsicOp op) {
4453 return setLoweringToLTL<ltl::NotOp>(op, getLoweredValue(op.getInput()));
4456LogicalResult FIRRTLLowering::visitExpr(LTLImplicationIntrinsicOp op) {
4457 return setLoweringToLTL<ltl::ImplicationOp>(
4459 ValueRange{getLoweredValue(op.getLhs()), getLoweredValue(op.getRhs())});
4462LogicalResult FIRRTLLowering::visitExpr(LTLUntilIntrinsicOp op) {
4463 return setLoweringToLTL<ltl::UntilOp>(
4465 ValueRange{getLoweredValue(op.getLhs()), getLoweredValue(op.getRhs())});
4468LogicalResult FIRRTLLowering::visitExpr(LTLEventuallyIntrinsicOp op) {
4469 return setLoweringToLTL<ltl::EventuallyOp>(op,
4470 getLoweredValue(op.getInput()));
4473LogicalResult FIRRTLLowering::visitExpr(LTLClockIntrinsicOp op) {
4474 return setLoweringToLTL<ltl::ClockOp>(op, getLoweredValue(op.getInput()),
4475 ltl::ClockEdge::Pos,
4476 getLoweredNonClockValue(op.getClock()));
4479template <
typename TargetOp,
typename IntrinsicOp>
4480LogicalResult FIRRTLLowering::lowerVerifIntrinsicOp(IntrinsicOp op) {
4481 auto property = getLoweredValue(op.getProperty());
4482 auto enable = op.getEnable() ? getLoweredValue(op.getEnable()) : Value();
4483 TargetOp::create(builder, property, enable, op.getLabelAttr());
4487LogicalResult FIRRTLLowering::visitStmt(VerifAssertIntrinsicOp op) {
4488 return lowerVerifIntrinsicOp<verif::AssertOp>(op);
4491LogicalResult FIRRTLLowering::visitStmt(VerifAssumeIntrinsicOp op) {
4492 return lowerVerifIntrinsicOp<verif::AssumeOp>(op);
4495LogicalResult FIRRTLLowering::visitStmt(VerifCoverIntrinsicOp op) {
4496 return lowerVerifIntrinsicOp<verif::CoverOp>(op);
4499LogicalResult FIRRTLLowering::visitStmt(VerifRequireIntrinsicOp op) {
4500 if (!isa<verif::ContractOp>(op->getParentOp()))
4501 return lowerVerifIntrinsicOp<verif::AssertOp>(op);
4502 return lowerVerifIntrinsicOp<verif::RequireOp>(op);
4505LogicalResult FIRRTLLowering::visitStmt(VerifEnsureIntrinsicOp op) {
4506 if (!isa<verif::ContractOp>(op->getParentOp()))
4507 return lowerVerifIntrinsicOp<verif::AssertOp>(op);
4508 return lowerVerifIntrinsicOp<verif::EnsureOp>(op);
4511LogicalResult FIRRTLLowering::visitExpr(HasBeenResetIntrinsicOp op) {
4512 auto clock = getLoweredNonClockValue(op.getClock());
4513 auto reset = getLoweredValue(op.getReset());
4514 if (!clock || !reset)
4516 auto resetType = op.getReset().getType();
4517 auto uintResetType = dyn_cast<UIntType>(resetType);
4518 auto isSync = uintResetType && uintResetType.getWidth() == 1;
4519 auto isAsync = isa<AsyncResetType>(resetType);
4520 if (!isAsync && !isSync) {
4521 auto d = op.emitError(
"uninferred reset passed to 'has_been_reset'; "
4522 "requires sync or async reset");
4523 d.attachNote() <<
"reset is of type " << resetType
4524 <<
", should be '!firrtl.uint<1>' or '!firrtl.asyncreset'";
4527 return setLoweringTo<verif::HasBeenResetOp>(op, clock, reset, isAsync);
4534LogicalResult FIRRTLLowering::visitExpr(BitsPrimOp op) {
4535 auto input = getLoweredValue(op.getInput());
4539 Type resultType = builder.getIntegerType(op.getHi() - op.getLo() + 1);
4540 return setLoweringTo<comb::ExtractOp>(op, resultType, input, op.getLo());
4543LogicalResult FIRRTLLowering::visitExpr(InvalidValueOp op) {
4544 auto resultTy =
lowerType(op.getType());
4551 if (type_isa<AnalogType>(op.getType()))
4554 return setLoweringTo<sv::WireOp>(op, resultTy,
".invalid_analog");
4557 if (type_cast<FIRRTLBaseType>(op.getType()).containsAnalog())
4568 auto constant = getOrCreateIntConstant(*bitwidth, 0);
4570 if (!type_isa<IntegerType>(resultTy))
4572 return setLowering(op, constant);
4576 op.emitOpError(
"unsupported type");
4580LogicalResult FIRRTLLowering::visitExpr(HeadPrimOp op) {
4581 auto input = getLoweredValue(op.getInput());
4584 auto inWidth = type_cast<IntegerType>(input.getType()).getWidth();
4585 if (op.getAmount() == 0)
4586 return setLowering(op, Value());
4587 Type resultType = builder.getIntegerType(op.getAmount());
4588 return setLoweringTo<comb::ExtractOp>(op, resultType, input,
4589 inWidth - op.getAmount());
4592LogicalResult FIRRTLLowering::visitExpr(ShlPrimOp op) {
4593 auto input = getLoweredValue(op.getInput());
4596 if (op.getAmount() == 0)
4598 return setLowering(op, getOrCreateIntConstant(op.getAmount(), 0));
4603 if (op.getAmount() == 0)
4604 return setLowering(op, input);
4606 auto zero = getOrCreateIntConstant(op.getAmount(), 0);
4607 return setLoweringTo<comb::ConcatOp>(op, input, zero);
4610LogicalResult FIRRTLLowering::visitExpr(ShrPrimOp op) {
4611 auto input = getLoweredValue(op.getInput());
4616 auto inWidth = type_cast<IntegerType>(input.getType()).getWidth();
4617 auto shiftAmount = op.getAmount();
4618 if (shiftAmount >= inWidth) {
4620 if (type_cast<IntType>(op.getInput().getType()).isUnsigned())
4621 return setLowering(op, {});
4624 shiftAmount = inWidth - 1;
4627 Type resultType = builder.getIntegerType(inWidth - shiftAmount);
4628 return setLoweringTo<comb::ExtractOp>(op, resultType, input, shiftAmount);
4631LogicalResult FIRRTLLowering::visitExpr(TailPrimOp op) {
4632 auto input = getLoweredValue(op.getInput());
4636 auto inWidth = type_cast<IntegerType>(input.getType()).getWidth();
4637 if (inWidth == op.getAmount())
4638 return setLowering(op, Value());
4639 Type resultType = builder.getIntegerType(inWidth - op.getAmount());
4640 return setLoweringTo<comb::ExtractOp>(op, resultType, input, 0);
4643LogicalResult FIRRTLLowering::visitExpr(MuxPrimOp 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 if (isa<ClockType>(op.getType()))
4651 return setLoweringTo<seq::ClockMuxOp>(op, cond, ifTrue, ifFalse);
4652 return setLoweringTo<comb::MuxOp>(op, ifTrue.getType(), cond, ifTrue, ifFalse,
4656LogicalResult FIRRTLLowering::visitExpr(Mux2CellIntrinsicOp op) {
4657 auto cond = getLoweredValue(op.getSel());
4658 auto ifTrue = getLoweredAndExtendedValue(op.getHigh(), op.getType());
4659 auto ifFalse = getLoweredAndExtendedValue(op.getLow(), op.getType());
4660 if (!cond || !ifTrue || !ifFalse)
4663 auto val = comb::MuxOp::create(builder, ifTrue.getType(), cond, ifTrue,
4665 return setLowering(op, createValueWithMuxAnnotation(val,
true));
4668LogicalResult FIRRTLLowering::visitExpr(Mux4CellIntrinsicOp op) {
4669 auto sel = getLoweredValue(op.getSel());
4670 auto v3 = getLoweredAndExtendedValue(op.getV3(), op.getType());
4671 auto v2 = getLoweredAndExtendedValue(op.getV2(), op.getType());
4672 auto v1 = getLoweredAndExtendedValue(op.getV1(), op.getType());
4673 auto v0 = getLoweredAndExtendedValue(op.getV0(), op.getType());
4674 if (!sel || !v3 || !v2 || !v1 || !v0)
4676 Value array[] = {v3, v2, v1, v0};
4679 return setLowering(op, createValueWithMuxAnnotation(val,
false));
4698Value FIRRTLLowering::createValueWithMuxAnnotation(Operation *op,
bool isMux2) {
4699 assert(op->getNumResults() == 1 &&
"only expect a single result");
4700 auto val = op->getResult(0);
4704 op, sv::SVAttributeAttr::get(builder.getContext(),
"cadence map_to_mux",
4711 OpBuilder::InsertionGuard guard(builder);
4712 builder.setInsertionPoint(op);
4713 StringRef namehint = isMux2 ?
"mux2cell_in" :
"mux4cell_in";
4714 for (
auto [idx, operand] :
llvm::enumerate(op->getOperands())) {
4716 op->getContext(),
nullptr, 0,
4719 hw::WireOp::create(builder, operand, namehint + Twine(idx), innerSym);
4720 op->setOperand(idx, wire);
4725 sv::setSVAttributes(assignOp,
4726 sv::SVAttributeAttr::get(builder.getContext(),
4727 "synopsys infer_mux_override",
4732Value FIRRTLLowering::createArrayIndexing(Value array, Value index) {
4734 auto size = hw::type_cast<hw::ArrayType>(array.getType()).getNumElements();
4739 if (!llvm::isPowerOf2_64(size)) {
4740 auto extElem = getOrCreateIntConstant(APInt(llvm::Log2_64_Ceil(size), 0));
4742 SmallVector<Value> temp(llvm::NextPowerOf2(size) - size, extValue);
4744 Value temp2[] = {ext.getResult(), array};
4750 return inBoundsRead;
4753LogicalResult FIRRTLLowering::visitExpr(MultibitMuxOp op) {
4755 auto index = getLoweredAndExtOrTruncValue(
4757 UIntType::get(op.getContext(),
4762 SmallVector<Value> loweredInputs;
4763 loweredInputs.reserve(op.getInputs().size());
4764 for (
auto input : op.getInputs()) {
4765 auto lowered = getLoweredAndExtendedValue(input, op.getType());
4768 loweredInputs.push_back(lowered);
4772 return setLowering(op, createArrayIndexing(array, index));
4775LogicalResult FIRRTLLowering::visitExpr(VerbatimExprOp op) {
4776 auto resultTy =
lowerType(op.getType());
4780 SmallVector<Value, 4> operands;
4781 operands.reserve(op.getSubstitutions().size());
4782 for (
auto operand : op.getSubstitutions()) {
4783 auto lowered = getLoweredValue(operand);
4786 operands.push_back(lowered);
4789 ArrayAttr symbols = op.getSymbolsAttr();
4791 symbols = ArrayAttr::get(op.getContext(), {});
4793 return setLoweringTo<sv::VerbatimExprOp>(op, resultTy, op.getTextAttr(),
4797LogicalResult FIRRTLLowering::visitExpr(XMRRefOp op) {
4801 Type baseType = op.getType().getType();
4804 if (isa<ClockType>(baseType))
4805 xmrType = builder.getIntegerType(1);
4809 return setLoweringTo<sv::XMRRefOp>(op, sv::InOutType::get(xmrType),
4810 op.getRef(), op.getVerbatimSuffixAttr());
4813LogicalResult FIRRTLLowering::visitExpr(XMRDerefOp op) {
4817 if (isa<ClockType>(op.getType()))
4818 xmrType = builder.getIntegerType(1);
4822 auto xmr = sv::XMRRefOp::create(builder, sv::InOutType::get(xmrType),
4823 op.getRef(), op.getVerbatimSuffixAttr());
4824 auto readXmr = getReadValue(xmr);
4825 if (!isa<ClockType>(op.getType()))
4826 return setLowering(op, readXmr);
4827 return setLoweringTo<seq::ToClockOp>(op, readXmr);
4832LogicalResult FIRRTLLowering::visitExpr(TimeOp op) {
return success(); }
4833LogicalResult FIRRTLLowering::visitExpr(HierarchicalModuleNameOp op) {
4841LogicalResult FIRRTLLowering::visitStmt(SkipOp op) {
4853FailureOr<bool> FIRRTLLowering::lowerConnect(Value destVal, Value srcVal) {
4854 auto srcType = srcVal.getType();
4855 auto dstType = destVal.getType();
4856 if (srcType != dstType &&
4857 (isa<hw::TypeAliasType>(srcType) || isa<hw::TypeAliasType>(dstType))) {
4860 return TypeSwitch<Operation *, FailureOr<bool>>(destVal.getDefiningOp())
4861 .Case<hw::WireOp>([&](
auto op) {
4862 maybeUnused(op.getInput());
4863 op.getInputMutable().assign(srcVal);
4866 .Case<seq::FirRegOp>([&](
auto op) {
4867 maybeUnused(op.getNext());
4868 op.getNextMutable().assign(srcVal);
4871 .Case<hw::StructExtractOp, hw::ArrayGetOp>([](
auto op) {
4874 op.emitOpError(
"used as connect destination");
4877 .Default([](
auto) {
return false; });
4880LogicalResult FIRRTLLowering::visitStmt(ConnectOp op) {
4881 auto dest = op.getDest();
4883 auto destType = type_cast<FIRRTLBaseType>(dest.getType()).getPassiveType();
4884 auto srcVal = getLoweredAndExtendedValue(op.getSrc(), destType);
4886 return handleZeroBit(op.getSrc(), []() { return success(); });
4888 auto destVal = getPossiblyInoutLoweredValue(dest);
4892 auto result = lowerConnect(destVal, srcVal);
4900 if (updateIfBackedge(destVal, srcVal))
4903 if (!isa<hw::InOutType>(destVal.getType()))
4904 return op.emitError(
"destination isn't an inout type");
4910LogicalResult FIRRTLLowering::visitStmt(MatchingConnectOp op) {
4911 auto dest = op.getDest();
4912 auto srcVal = getLoweredValue(op.getSrc());
4914 return handleZeroBit(op.getSrc(), []() { return success(); });
4916 auto destVal = getPossiblyInoutLoweredValue(dest);
4920 auto result = lowerConnect(destVal, srcVal);
4928 if (updateIfBackedge(destVal, srcVal))
4931 if (!isa<hw::InOutType>(destVal.getType()))
4932 return op.emitError(
"destination isn't an inout type");
4938LogicalResult FIRRTLLowering::visitStmt(ForceOp op) {
4939 auto srcVal = getLoweredValue(op.getSrc());
4943 auto destVal = getPossiblyInoutLoweredValue(op.getDest());
4947 if (!isa<hw::InOutType>(destVal.getType()))
4948 return op.emitError(
"destination isn't an inout type");
4951 circuitState.addMacroDecl(builder.getStringAttr(
"SYNTHESIS"));
4952 addToIfDefBlock(
"SYNTHESIS", std::function<
void()>(), [&]() {
4953 addToInitialBlock([&]() { sv::ForceOp::create(builder, destVal, srcVal); });
4958LogicalResult FIRRTLLowering::visitStmt(RefForceOp op) {
4959 auto src = getLoweredNonClockValue(op.getSrc());
4960 auto clock = getLoweredNonClockValue(op.getClock());
4961 auto pred = getLoweredValue(op.getPredicate());
4962 if (!src || !clock || !pred)
4965 auto destVal = getPossiblyInoutLoweredValue(op.getDest());
4970 circuitState.addMacroDecl(builder.getStringAttr(
"SYNTHESIS"));
4971 addToIfDefBlock(
"SYNTHESIS", std::function<
void()>(), [&]() {
4972 addToAlwaysBlock(clock, [&]() {
4973 addIfProceduralBlock(
4974 pred, [&]() { sv::ForceOp::create(builder, destVal, src); });
4979LogicalResult FIRRTLLowering::visitStmt(RefForceInitialOp op) {
4980 auto src = getLoweredNonClockValue(op.getSrc());
4981 auto pred = getLoweredValue(op.getPredicate());
4985 auto destVal = getPossiblyInoutLoweredValue(op.getDest());
4990 circuitState.addMacroDecl(builder.getStringAttr(
"SYNTHESIS"));
4991 addToIfDefBlock(
"SYNTHESIS", std::function<
void()>(), [&]() {
4992 addToInitialBlock([&]() {
4993 addIfProceduralBlock(
4994 pred, [&]() { sv::ForceOp::create(builder, destVal, src); });
4999LogicalResult FIRRTLLowering::visitStmt(RefReleaseOp op) {
5000 auto clock = getLoweredNonClockValue(op.getClock());
5001 auto pred = getLoweredValue(op.getPredicate());
5002 if (!clock || !pred)
5005 auto destVal = getPossiblyInoutLoweredValue(op.getDest());
5010 circuitState.addMacroDecl(builder.getStringAttr(
"SYNTHESIS"));
5011 addToIfDefBlock(
"SYNTHESIS", std::function<
void()>(), [&]() {
5012 addToAlwaysBlock(clock, [&]() {
5013 addIfProceduralBlock(pred,
5014 [&]() { sv::ReleaseOp::create(builder, destVal); });
5019LogicalResult FIRRTLLowering::visitStmt(RefReleaseInitialOp op) {
5020 auto destVal = getPossiblyInoutLoweredValue(op.getDest());
5021 auto pred = getLoweredValue(op.getPredicate());
5022 if (!destVal || !pred)
5026 circuitState.addMacroDecl(builder.getStringAttr(
"SYNTHESIS"));
5027 addToIfDefBlock(
"SYNTHESIS", std::function<
void()>(), [&]() {
5028 addToInitialBlock([&]() {
5029 addIfProceduralBlock(pred,
5030 [&]() { sv::ReleaseOp::create(builder, destVal); });
5038 StringRef originalFormatString,
5039 ValueRange operands,
5040 StringAttr &result) {
5043 SmallString<32> formatString;
5044 for (
size_t i = 0, e = originalFormatString.size(), subIdx = 0; i != e; ++i) {
5045 char c = originalFormatString[i];
5049 formatString.push_back(c);
5052 SmallString<6> width;
5053 c = originalFormatString[++i];
5056 c = originalFormatString[++i];
5067 formatString.append(width);
5073 formatString.push_back(c);
5080 if (originalFormatString.slice(i, i + 4) !=
"{{}}") {
5081 formatString.push_back(c);
5085 auto substitution = operands[subIdx++];
5086 assert(type_isa<FStringType>(substitution.getType()) &&
5087 "the operand for a '{{}}' substitution must be an 'fstring' type");
5089 TypeSwitch<Operation *, LogicalResult>(substitution.getDefiningOp())
5090 .template Case<TimeOp>([&](
auto) {
5091 formatString.append(
"%0t");
5094 .
template Case<HierarchicalModuleNameOp>([&](
auto) {
5095 formatString.append(
"%m");
5098 .Default([&](
auto) {
5099 emitError(loc,
"has a substitution with an unimplemented "
5101 .attachNote(substitution.getLoc())
5102 <<
"op with an unimplemented lowering is here";
5112 formatString.push_back(c);
5116 result = StringAttr::get(loc->getContext(), formatString);
5123LogicalResult FIRRTLLowering::visitPrintfLike(
5124 T op,
const FileDescriptorInfo &fileDescriptorInfo,
bool usePrintfCond) {
5125 auto clock = getLoweredNonClockValue(op.getClock());
5126 auto cond = getLoweredValue(op.getCond());
5127 if (!clock || !cond)
5130 StringAttr formatString;
5132 op.getSubstitutions(), formatString)))
5135 auto fn = [&](Value fd) {
5136 SmallVector<Value> operands;
5137 if (failed(loweredFmtOperands(op.getSubstitutions(), operands)))
5139 sv::FWriteOp::create(builder, op.getLoc(), fd, formatString, operands);
5143 return lowerStatementWithFd(fileDescriptorInfo, clock, cond, fn,
5147LogicalResult FIRRTLLowering::visitStmt(FPrintFOp op) {
5148 StringAttr outputFileAttr;
5150 op.getOutputFileSubstitutions(),
5154 FileDescriptorInfo outputFile(outputFileAttr,
5155 op.getOutputFileSubstitutions());
5156 return visitPrintfLike(op, outputFile,
false);
5160LogicalResult FIRRTLLowering::visitStmt(FFlushOp op) {
5161 auto clock = getLoweredNonClockValue(op.getClock());
5162 auto cond = getLoweredValue(op.getCond());
5163 if (!clock || !cond)
5166 auto fn = [&](Value fd) {
5167 sv::FFlushOp::create(builder, op.getLoc(), fd);
5171 if (!op.getOutputFileAttr())
5172 return lowerStatementWithFd({}, clock, cond, fn,
false);
5176 StringAttr outputFileAttr;
5178 op.getOutputFileSubstitutions(),
5182 return lowerStatementWithFd(
5183 FileDescriptorInfo(outputFileAttr, op.getOutputFileSubstitutions()),
5184 clock, cond, fn,
false);
5189LogicalResult FIRRTLLowering::visitStmt(StopOp op) {
5190 auto clock = getLoweredValue(op.getClock());
5191 auto cond = getLoweredValue(op.getCond());
5192 if (!clock || !cond)
5195 circuitState.usedStopCond =
true;
5196 circuitState.addFragment(theModule,
"STOP_COND_FRAGMENT");
5199 sv::MacroRefExprOp::create(builder, cond.getType(),
"STOP_COND_");
5200 Value exitCond = builder.createOrFold<
comb::AndOp>(stopCond, cond,
true);
5202 sim::ClockedTerminateOp::create(builder, clock, exitCond,
5203 op.getExitCode() == 0,
5212template <
typename... Args>
5214 StringRef opName, Args &&...args) {
5215 if (opName ==
"assert")
5216 return sv::AssertOp::create(builder, std::forward<Args>(args)...);
5217 if (opName ==
"assume")
5218 return sv::AssumeOp::create(builder, std::forward<Args>(args)...);
5219 if (opName ==
"cover")
5220 return sv::CoverOp::create(builder, std::forward<Args>(args)...);
5221 llvm_unreachable(
"unknown verification op");
5227template <
typename... Args>
5229 StringRef opName, Args &&...args) {
5230 if (opName ==
"assert")
5231 return sv::AssertConcurrentOp::create(builder, std::forward<Args>(args)...);
5232 if (opName ==
"assume")
5233 return sv::AssumeConcurrentOp::create(builder, std::forward<Args>(args)...);
5234 if (opName ==
"cover")
5235 return sv::CoverConcurrentOp::create(builder, std::forward<Args>(args)...);
5236 llvm_unreachable(
"unknown verification op");
5257LogicalResult FIRRTLLowering::lowerVerificationStatement(
5258 Operation *op, StringRef labelPrefix, Value opClock, Value opPredicate,
5259 Value opEnable, StringAttr opMessageAttr, ValueRange opOperands,
5260 StringAttr opNameAttr,
bool isConcurrent, EventControl opEventControl) {
5261 StringRef opName = op->getName().stripDialect();
5264 ArrayRef<Attribute> guards{};
5265 if (
auto guardsAttr = op->template getAttrOfType<ArrayAttr>(
"guards"))
5266 guards = guardsAttr.getValue();
5268 auto isCover = isa<CoverOp>(op);
5269 auto clock = getLoweredNonClockValue(opClock);
5270 auto enable = getLoweredValue(opEnable);
5271 auto predicate = getLoweredValue(opPredicate);
5272 if (!clock || !enable || !predicate)
5276 if (opNameAttr && !opNameAttr.getValue().empty())
5278 StringAttr prefixedLabel;
5281 StringAttr::get(builder.getContext(), labelPrefix + label.getValue());
5284 SmallVector<Value> messageOps;
5288 if (flavor == VerificationFlavor::IfElseFatal && !isa<AssertOp>(op))
5289 flavor = VerificationFlavor::None;
5291 if (flavor == VerificationFlavor::None) {
5295 auto format = op->getAttrOfType<StringAttr>(
"format");
5297 if (isConcurrent && format && format.getValue() ==
"ifElseFatal") {
5298 if (!isa<AssertOp>(op))
5299 return op->emitError()
5300 <<
"ifElseFatal format cannot be used for non-assertions";
5301 flavor = VerificationFlavor::IfElseFatal;
5302 }
else if (isConcurrent)
5303 flavor = VerificationFlavor::SVA;
5305 flavor = VerificationFlavor::Immediate;
5308 if (!isCover && opMessageAttr && !opMessageAttr.getValue().empty()) {
5312 opOperands, message)))
5315 if (failed(loweredFmtOperands(opOperands, messageOps)))
5318 if (flavor == VerificationFlavor::SVA) {
5323 for (
auto &loweredValue : messageOps)
5324 loweredValue =
sv::SampledOp::create(builder, loweredValue);
5330 case VerificationFlavor::Immediate: {
5332 auto deferImmediate = circt::sv::DeferAssertAttr::get(
5333 builder.getContext(), circt::sv::DeferAssert::Immediate);
5334 addToAlwaysBlock(clock, [&]() {
5335 addIfProceduralBlock(enable, [&]() {
5337 prefixedLabel, message, messageOps);
5342 case VerificationFlavor::IfElseFatal: {
5343 assert(isa<AssertOp>(op) &&
"only assert is expected");
5346 auto boolType = IntegerType::get(builder.getContext(), 1);
5347 predicate = comb::createOrFoldNot(predicate, builder,
true);
5348 predicate = builder.createOrFold<
comb::AndOp>(enable, predicate,
true);
5350 circuitState.addMacroDecl(builder.getStringAttr(
"SYNTHESIS"));
5351 addToIfDefBlock(
"SYNTHESIS", {}, [&]() {
5352 addToAlwaysBlock(clock, [&]() {
5353 addIfProceduralBlock(predicate, [&]() {
5354 circuitState.usedStopCond =
true;
5355 circuitState.addFragment(theModule,
"STOP_COND_FRAGMENT");
5357 circuitState.usedAssertVerboseCond =
true;
5358 circuitState.addFragment(theModule,
"ASSERT_VERBOSE_COND_FRAGMENT");
5360 addIfProceduralBlock(
5361 sv::MacroRefExprOp::create(builder, boolType,
5362 "ASSERT_VERBOSE_COND_"),
5364 sv::ErrorProceduralOp::create(builder, message, messageOps);
5366 addIfProceduralBlock(
5367 sv::MacroRefExprOp::create(builder, boolType,
"STOP_COND_"),
5368 [&]() { sv::FatalProceduralOp::create(builder); });
5374 case VerificationFlavor::SVA: {
5379 comb::createOrFoldNot(enable, builder,
true);
5381 builder.createOrFold<
comb::OrOp>(notEnable, predicate,
true);
5383 predicate = builder.createOrFold<
comb::AndOp>(enable, predicate,
true);
5387 sv::EventControl event;
5388 switch (opEventControl) {
5389 case EventControl::AtPosEdge:
5390 event = circt::sv::EventControl::AtPosEdge;
5392 case EventControl::AtEdge:
5393 event = circt::sv::EventControl::AtEdge;
5395 case EventControl::AtNegEdge:
5396 event = circt::sv::EventControl::AtNegEdge;
5402 circt::sv::EventControlAttr::get(builder.getContext(), event), clock,
5403 predicate, prefixedLabel, message, messageOps);
5406 case VerificationFlavor::None:
5408 "flavor `None` must be converted into one of concreate flavors");
5415 return emitGuards(op->getLoc(), guards,
emit);
5419LogicalResult FIRRTLLowering::visitStmt(AssertOp op) {
5420 return lowerVerificationStatement(
5421 op,
"assert__", op.getClock(), op.getPredicate(), op.getEnable(),
5422 op.getMessageAttr(), op.getSubstitutions(), op.getNameAttr(),
5423 op.getIsConcurrent(), op.getEventControl());
5427LogicalResult FIRRTLLowering::visitStmt(AssumeOp op) {
5428 return lowerVerificationStatement(
5429 op,
"assume__", op.getClock(), op.getPredicate(), op.getEnable(),
5430 op.getMessageAttr(), op.getSubstitutions(), op.getNameAttr(),
5431 op.getIsConcurrent(), op.getEventControl());
5435LogicalResult FIRRTLLowering::visitStmt(CoverOp op) {
5436 return lowerVerificationStatement(
5437 op,
"cover__", op.getClock(), op.getPredicate(), op.getEnable(),
5438 op.getMessageAttr(), op.getSubstitutions(), op.getNameAttr(),
5439 op.getIsConcurrent(), op.getEventControl());
5443LogicalResult FIRRTLLowering::visitStmt(UnclockedAssumeIntrinsicOp op) {
5448 auto guardsAttr = op->getAttrOfType<mlir::ArrayAttr>(
"guards");
5449 ArrayRef<Attribute> guards =
5450 guardsAttr ? guardsAttr.getValue() : ArrayRef<Attribute>();
5452 auto label = op.getNameAttr();
5453 StringAttr assumeLabel;
5454 if (label && !label.empty())
5456 StringAttr::get(builder.getContext(),
"assume__" + label.getValue());
5457 auto predicate = getLoweredValue(op.getPredicate());
5458 auto enable = getLoweredValue(op.getEnable());
5459 auto notEnable = comb::createOrFoldNot(enable, builder,
true);
5460 predicate = builder.createOrFold<
comb::OrOp>(notEnable, predicate,
true);
5462 SmallVector<Value> messageOps;
5463 for (
auto operand : op.getSubstitutions()) {
5464 auto loweredValue = getLoweredValue(operand);
5465 if (!loweredValue) {
5469 loweredValue = getOrCreateIntConstant(1, 0);
5471 messageOps.push_back(loweredValue);
5473 return emitGuards(op.getLoc(), guards, [&]() {
5474 sv::AlwaysOp::create(
5475 builder, ArrayRef(sv::EventControl::AtEdge), ArrayRef(predicate),
5477 if (op.getMessageAttr().getValue().empty())
5478 buildImmediateVerifOp(
5479 builder,
"assume", predicate,
5480 circt::sv::DeferAssertAttr::get(
5481 builder.getContext(), circt::sv::DeferAssert::Immediate),
5484 buildImmediateVerifOp(
5485 builder,
"assume", predicate,
5486 circt::sv::DeferAssertAttr::get(
5487 builder.getContext(), circt::sv::DeferAssert::Immediate),
5488 assumeLabel, op.getMessageAttr(), messageOps);
5493LogicalResult FIRRTLLowering::visitStmt(AttachOp op) {
5495 if (op.getAttached().size() < 2)
5498 SmallVector<Value, 4> inoutValues;
5499 for (
auto v : op.getAttached()) {
5500 inoutValues.push_back(getPossiblyInoutLoweredValue(v));
5501 if (!inoutValues.back()) {
5505 inoutValues.pop_back();
5509 if (!isa<hw::InOutType>(inoutValues.back().getType()))
5510 return op.emitError(
"operand isn't an inout type");
5513 if (inoutValues.size() < 2)
5524 bool isAttachInternalOnly =
5525 llvm::none_of(inoutValues, [](
auto v) {
return isa<BlockArgument>(v); });
5527 if (isAttachInternalOnly) {
5528 auto v0 = inoutValues.front();
5529 for (
auto v : inoutValues) {
5532 v.replaceAllUsesWith(v0);
5539 circuitState.addMacroDecl(builder.getStringAttr(
"SYNTHESIS"));
5540 circuitState.addMacroDecl(builder.getStringAttr(
"VERILATOR"));
5545 SmallVector<Value, 4> values;
5546 for (
auto inoutValue : inoutValues)
5547 values.push_back(getReadValue(inoutValue));
5549 for (
size_t i1 = 0, e = inoutValues.size(); i1 != e; ++i1) {
5550 for (
size_t i2 = 0; i2 != e; ++i2)
5558 sv::IfDefOp::create(
5559 builder,
"VERILATOR",
5561 sv::VerbatimOp::create(
5563 "`error \"Verilator does not support alias and thus "
5565 "arbitrarily connect bidirectional wires and ports\"");
5567 [&]() { sv::AliasOp::create(builder, inoutValues); });
5573LogicalResult FIRRTLLowering::visitStmt(BindOp op) {
5574 sv::BindOp::create(builder, op.getInstanceAttr());
5578LogicalResult FIRRTLLowering::fixupLTLOps() {
5579 if (ltlOpFixupWorklist.empty())
5581 LLVM_DEBUG(llvm::dbgs() <<
"Fixing up " << ltlOpFixupWorklist.size()
5585 for (
unsigned i = 0, e = ltlOpFixupWorklist.size(); i != e; ++i)
5586 for (
auto *user : ltlOpFixupWorklist[i]->getUsers())
5587 if (isa<
hw::WireOp>(user))
5588 ltlOpFixupWorklist.insert(user);
5591 while (!ltlOpFixupWorklist.empty()) {
5592 auto *op = ltlOpFixupWorklist.pop_back_val();
5595 if (
auto opIntf = dyn_cast_or_null<mlir::InferTypeOpInterface>(op)) {
5596 LLVM_DEBUG(llvm::dbgs() <<
"- Update " << *op <<
"\n");
5597 SmallVector<Type, 2> types;
5598 auto result = opIntf.inferReturnTypes(
5599 op->getContext(), op->getLoc(), op->getOperands(),
5600 op->getAttrDictionary(), op->getPropertiesStorage(), op->getRegions(),
5604 assert(types.size() == op->getNumResults());
5608 for (
auto [result, type] :
llvm::zip(op->getResults(), types)) {
5609 if (result.getType() == type)
5611 LLVM_DEBUG(llvm::dbgs()
5612 <<
" - Result #" << result.getResultNumber() <<
" from "
5613 << result.getType() <<
" to " << type <<
"\n");
5614 result.setType(type);
5615 for (
auto *user : result.getUsers())
5617 ltlOpFixupWorklist.insert(user);
5622 if (
auto wireOp = dyn_cast<hw::WireOp>(op)) {
5623 if (isa<ltl::SequenceType, ltl::PropertyType>(wireOp.getType())) {
5624 wireOp.replaceAllUsesWith(wireOp.getInput());
5625 LLVM_DEBUG(llvm::dbgs() <<
"- Remove " << wireOp <<
"\n");
5626 if (wireOp.use_empty())
5633 SmallPtrSet<Operation *, 4> usersReported;
5634 for (
auto *user : op->getUsers()) {
5635 if (!usersReported.insert(user).second)
5637 if (isa_and_nonnull<ltl::LTLDialect, verif::VerifDialect>(
5638 user->getDialect()))
5640 if (isa<hw::WireOp>(user))
5642 auto d = op->emitError(
5643 "verification operation used in a non-verification context");
5644 d.attachNote(user->getLoc())
5645 <<
"leaking outside verification context here";
assert(baseType &&"element must be base type")
static std::unique_ptr< Context > context
static LogicalResult emitFile(ArrayRef< Operation * > operations, StringRef filePath, raw_ostream &os)
Emits the given operation to a file represented by the passed ostream and file-path.
static void lowerModuleBody(FModuleOp mod, const DenseMap< StringAttr, PortConversion > &ports)
static Operation * buildImmediateVerifOp(ImplicitLocOpBuilder &builder, StringRef opName, Args &&...args)
Helper function to build an immediate assert operation based on the original FIRRTL operation name.
static Operation * buildConcurrentVerifOp(ImplicitLocOpBuilder &builder, StringRef opName, Args &&...args)
Helper function to build a concurrent assert operation based on the original FIRRTL operation name.
static unsigned getBitWidthFromVectorSize(unsigned size)
static void moveVerifAnno(ModuleOp top, AnnotationSet &annos, StringRef annoClass, StringRef attrBase)
Move a ExtractTestCode related annotation from annotations to an attribute.
static Value castToFIRRTLType(Value val, Type type, ImplicitLocOpBuilder &builder)
Cast a value to a desired target type.
static ArrayAttr getHWParameters(FExtModuleOp module, bool ignoreValues)
Map the parameter specifier on the specified extmodule into the HWModule representation for parameter...
static bool isZeroBitFIRRTLType(Type type)
Return true if the specified type is a sized FIRRTL type (Int or Analog) with zero bits.
static Value tryEliminatingAttachesToAnalogValue(Value value, Operation *insertPoint)
Given a value of analog type, check to see the only use of it is an attach.
static LogicalResult handleZeroBit(Value failedOperand, const std::function< LogicalResult()> &fn)
Zero bit operands end up looking like failures from getLoweredValue.
static const char moduleHierarchyFileAttrName[]
Attribute that indicates that the module hierarchy starting at the annotated module should be dumped ...
static void tryCopyName(Operation *dst, Operation *src)
static LogicalResult verifyOpLegality(Operation *op)
This verifies that the target operation has been lowered to a legal operation.
static Value castFromFIRRTLType(Value val, Type type, ImplicitLocOpBuilder &builder)
Cast from a FIRRTL type (potentially with a flip) to a standard type.
static SmallVector< SubfieldOp > getAllFieldAccesses(Value structValue, StringRef field)
static Value tryEliminatingConnectsToValue(Value flipValue, Operation *insertPoint, CircuitLoweringState &loweringState)
Given a value of flip type, check to see if all of the uses of it are connects.
static LogicalResult resolveFormatString(Location loc, StringRef originalFormatString, ValueRange operands, StringAttr &result)
static Value getSingleNonInstanceOperand(AttachOp op)
static IntType getWidestIntType(Type t1, Type t2)
Given two FIRRTL integer types, return the widest one.
static FailureOr< VectorizeOp > lowerBody(VectorizeOp op)
Vectorizes the body of the given arc.vectorize operation if it is not already vectorized.
static Location getLoc(DefSlot slot)
static Block * getBodyBlock(FModuleLike mod)
std::shared_ptr< calyx::CalyxLoweringState > loweringState
Instantiate one of these and use it to build typed backedges.
void abandon()
Abandon the backedges, suppressing any diagnostics if they are still active upon destruction of the b...
Backedge get(mlir::Type resultType, mlir::LocationAttr optionalLoc={})
Create a typed backedge.
mlir::LogicalResult clearOrEmitError()
Clear the backedges, erasing any remaining cursor ops.
Backedge is a wrapper class around a Value.
void setValue(mlir::Value)
A namespace that is used to store existing names and generate new names in some scope within the IR.
This class provides a read-only projection over the MLIR attributes that represent a set of annotatio...
bool removeAnnotations(llvm::function_ref< bool(Annotation)> predicate)
Remove all annotations from this annotation set for which predicate returns true.
bool removeAnnotation(Annotation anno)
Remove an annotation from this annotation set.
Annotation getAnnotation(StringRef className) const
If this annotation set has an annotation with the specified class name, return it.
This class provides a read-only projection of an annotation.
DictionaryAttr getDict() const
Get the data dictionary of this attribute.
AttrClass getMember(StringAttr name) const
Return a member of the annotation.
bool isClass(Args... names) const
Return true if this annotation matches any of the specified class names.
FIRRTLVisitor allows you to visit all of the expr/stmt/decls with one class declaration.
ResultType visitInvalidOp(Operation *op, ExtraArgs... args)
visitInvalidOp is an override point for non-FIRRTL dialect operations.
ResultType visitUnhandledOp(Operation *op, ExtraArgs... args)
visitUnhandledOp is an override point for FIRRTL dialect ops that the concrete visitor didn't bother ...
This graph tracks modules and where they are instantiated.
FModuleLike getTopLevelModule()
Get the module corresponding to the top-level module of a circuit.
This is the common base class between SIntType and UIntType.
This table tracks nlas and what modules participate in them.
This is an edge in the InstanceGraph.
create(elements, Type result_type=None)
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.
FIRRTLBaseType getBaseType(Type type)
If it is a base type, return it as is.
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.
bool hasDroppableName(Operation *op)
Return true if the name is droppable.
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.
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