13 #include "../PassDetail.h"
35 #include "mlir/IR/BuiltinOps.h"
36 #include "mlir/IR/BuiltinTypes.h"
37 #include "mlir/IR/ImplicitLocOpBuilder.h"
38 #include "mlir/IR/Threading.h"
39 #include "mlir/Pass/Pass.h"
40 #include "llvm/ADT/StringSet.h"
41 #include "llvm/ADT/TinyPtrVector.h"
42 #include "llvm/Support/Debug.h"
43 #include "llvm/Support/FormatVariadic.h"
44 #include "llvm/Support/Parallel.h"
46 #define DEBUG_TYPE "lower-to-hw"
48 using namespace circt;
49 using namespace firrtl;
50 using circt::comb::ICmpPredicate;
59 auto ftype = dyn_cast<FIRRTLBaseType>(type);
60 return ftype && ftype.getPassiveType().getBitWidthOrSentinel() == 0;
67 for (
auto operand : op.getAttached()) {
69 operand.getDefiningOp<InstanceOp>())
73 if (!operand.hasOneUse() || singleSource)
75 singleSource = operand;
84 auto checkTypes = [](Operation *op) -> WalkResult {
86 if (isa_and_nonnull<FIRRTLDialect>(op->getDialect()))
87 return op->emitError(
"Found unhandled FIRRTL operation '")
88 << op->getName() <<
"'";
91 auto checkTypeRange = [&](TypeRange types) -> LogicalResult {
92 if (llvm::any_of(types, [](Type type) {
93 return isa<FIRRTLDialect>(type.getDialect());
95 return op->emitOpError(
"found unhandled FIRRTL type");
100 if (failed(checkTypeRange(op->getOperandTypes())) ||
101 failed(checkTypeRange(op->getResultTypes())))
102 return WalkResult::interrupt();
105 for (
auto ®ion : op->getRegions())
106 for (
auto &block : region)
107 if (failed(checkTypeRange(block.getArgumentTypes())))
108 return WalkResult::interrupt();
111 return WalkResult::advance();
114 if (checkTypes(op).wasInterrupted() || op->walk(checkTypes).wasInterrupted())
121 auto t1c = type_cast<IntType>(t1), t2c = type_cast<IntType>(t2);
122 return t2c.getWidth() > t1c.getWidth() ? t2c : t1c;
128 ImplicitLocOpBuilder &
builder) {
130 if (BundleType bundle = dyn_cast<BundleType>(type))
131 val =
builder.createOrFold<HWStructCastOp>(bundle.getPassiveType(), val);
133 if (type != val.getType())
134 val =
builder.create<mlir::UnrealizedConversionCastOp>(type, val).getResult(
142 ImplicitLocOpBuilder &
builder) {
144 if (hw::StructType structTy = type.dyn_cast<hw::StructType>()) {
148 .create<mlir::UnrealizedConversionCastOp>(
149 type_cast<FIRRTLBaseType>(val.getType()).getPassiveType(), val)
151 val =
builder.createOrFold<HWStructCastOp>(type, val);
156 builder.create<mlir::UnrealizedConversionCastOp>(type, val).getResult(0);
163 StringRef annoClass, StringRef attrBase) {
165 auto ctx = top.getContext();
168 if (
auto dir = anno.getMember<StringAttr>(
"directory")) {
169 SmallVector<NamedAttribute> old;
170 for (
auto i : top->getAttrs())
174 hw::OutputFileAttr::getAsDirectory(ctx, dir.getValue(),
true,
true));
177 if (
auto file = anno.getMember<StringAttr>(
"filename")) {
178 SmallVector<NamedAttribute> old;
179 for (
auto i : top->getAttrs())
182 hw::OutputFileAttr::getFromFilename(
183 ctx, file.getValue(),
true));
189 return size == 1 ? 1 : llvm::Log2_64_Ceil(
size);
195 if (
auto attr = src->getAttrOfType<StringAttr>(
"name"))
196 if (!dst->hasAttr(
"sv.namehint") && !dst->hasAttr(
"name"))
197 dst->setAttr(
"sv.namehint", attr);
205 struct FIRRTLModuleLowering;
208 struct CircuitLoweringState {
209 std::atomic<bool> used_PRINTF_COND{
false};
210 std::atomic<bool> used_ASSERT_VERBOSE_COND{
false};
211 std::atomic<bool> used_STOP_COND{
false};
213 std::atomic<bool> used_RANDOMIZE_REG_INIT{
false},
214 used_RANDOMIZE_MEM_INIT{
false};
215 std::atomic<bool> used_RANDOMIZE_GARBAGE_ASSIGN{
false};
217 CircuitLoweringState(CircuitOp circuitOp,
bool enableAnnotationWarning,
218 bool emitChiselAssertsAsSVA,
220 : circuitOp(circuitOp), instanceGraph(instanceGraph),
221 enableAnnotationWarning(enableAnnotationWarning),
222 emitChiselAssertsAsSVA(emitChiselAssertsAsSVA), nlaTable(nlaTable) {
223 auto *context = circuitOp.getContext();
228 auto dirName = tbAnno.getMember<StringAttr>(
"dirname");
229 testBenchDirectory = hw::OutputFileAttr::getAsDirectory(
230 context, dirName.getValue(),
false,
true);
233 for (
auto &op : *circuitOp.getBodyBlock()) {
234 if (
auto module = dyn_cast<FModuleLike>(op))
245 testHarness =
nullptr;
246 }
else if (dut == testHarness) {
247 testHarness =
nullptr;
251 Operation *getNewModule(Operation *oldModule) {
252 auto it = oldToNewModuleMap.find(oldModule);
253 return it != oldToNewModuleMap.end() ? it->second :
nullptr;
258 void processRemainingAnnotations(Operation *op,
const AnnotationSet &annoSet);
264 void addBind(sv::BindOp op) {
265 std::lock_guard<std::mutex> lock(bindsMutex);
271 hw::TypeAliasType getTypeAlias(Type rawType, BaseTypeAliasType firAliasType,
274 auto hwAlias = typeAliases.getTypedecl(firAliasType);
277 assert(!typeAliases.isFrozen() &&
278 "type aliases cannot be generated after its frozen");
279 return typeAliases.addTypedecl(rawType, firAliasType, typeLoc);
282 FModuleLike getDut() {
return dut; }
283 FModuleLike getTestHarness() {
return testHarness; }
287 bool isInDUT(igraph::ModuleOpInterface child) {
288 if (
auto parent = dyn_cast<igraph::ModuleOpInterface>(*dut))
289 return getInstanceGraph()->isAncestor(child, parent);
293 hw::OutputFileAttr getTestBenchDirectory() {
return testBenchDirectory; }
298 bool isInTestHarness(igraph::ModuleOpInterface mod) {
return !isInDUT(mod); }
305 Type
lowerType(Type type, Location loc) {
307 [&](Type rawType, BaseTypeAliasType firrtlType,
308 Location typeLoc) -> hw::TypeAliasType {
309 return getTypeAlias(rawType, firrtlType, typeLoc);
314 friend struct FIRRTLModuleLowering;
315 friend struct FIRRTLLowering;
316 CircuitLoweringState(
const CircuitLoweringState &) =
delete;
317 void operator=(
const CircuitLoweringState &) =
delete;
319 DenseMap<Operation *, Operation *> oldToNewModuleMap;
327 StringSet<> pendingAnnotations;
328 const bool enableAnnotationWarning;
329 std::mutex annotationPrintingMtx;
331 const bool emitChiselAssertsAsSVA;
335 SmallVector<sv::BindOp> binds;
338 std::mutex bindsMutex;
346 FModuleLike testHarness;
349 hw::OutputFileAttr testBenchDirectory;
353 DenseMap<std::pair<Attribute, Attribute>, Attribute> instanceForceNames;
367 struct RecordTypeAlias {
369 RecordTypeAlias(CircuitOp c) : circuitOp(c) {}
371 hw::TypeAliasType getTypedecl(BaseTypeAliasType firAlias)
const {
372 auto iter = firrtlTypeToAliasTypeMap.find(firAlias);
373 if (iter != firrtlTypeToAliasTypeMap.end())
378 bool isFrozen() {
return frozen; }
380 void freeze() { frozen =
true; }
382 hw::TypeAliasType addTypedecl(Type rawType, BaseTypeAliasType firAlias,
384 assert(!frozen &&
"Record already frozen, cannot be updated");
387 auto b = ImplicitLocOpBuilder::atBlockBegin(
389 &circuitOp->getParentRegion()->getBlocks().back());
390 typeScope = b.create<hw::TypeScopeOp>(
391 b.getStringAttr(circuitOp.getName() +
"__TYPESCOPE_"));
392 typeScope.getBodyRegion().push_back(
new Block());
394 auto typeName = firAlias.getName();
400 typeDeclNamespace.newName(typeName.getValue()));
402 auto typeScopeBuilder =
403 ImplicitLocOpBuilder::atBlockEnd(typeLoc, typeScope.getBodyBlock());
404 auto typeDecl = typeScopeBuilder.create<hw::TypedeclOp>(typeLoc, typeName,
408 {FlatSymbolRefAttr::get(typeDecl)}),
410 auto insert = firrtlTypeToAliasTypeMap.try_emplace(firAlias, hwAlias);
411 assert(insert.second &&
"Entry already exists, insert failed");
412 return insert.first->second;
418 hw::TypeScopeOp typeScope;
421 DenseMap<Type, hw::TypeAliasType> firrtlTypeToAliasTypeMap;
429 RecordTypeAlias typeAliases = RecordTypeAlias(circuitOp);
432 void CircuitLoweringState::processRemainingAnnotations(
434 if (!enableAnnotationWarning || annoSet.
empty())
436 std::lock_guard<std::mutex> lock(annotationPrintingMtx);
438 for (
auto a : annoSet) {
439 auto inserted = pendingAnnotations.insert(a.getClass());
440 if (!inserted.second)
480 mlir::emitWarning(op->getLoc(),
"unprocessed annotation:'" + a.getClass() +
481 "' still remaining after LowerToHW");
487 struct FIRRTLModuleLowering :
public LowerFIRRTLToHWBase<FIRRTLModuleLowering> {
489 void runOnOperation()
override;
490 void setDisableMemRandomization() { disableMemRandomization =
true; }
491 void setDisableRegRandomization() { disableRegRandomization =
true; }
492 void setEnableAnnotationWarning() { enableAnnotationWarning =
true; }
493 void setEmitChiselAssertAsSVA() { emitChiselAssertsAsSVA =
true; }
496 void lowerFileHeader(CircuitOp op, CircuitLoweringState &loweringState);
497 LogicalResult lowerPorts(ArrayRef<PortInfo> firrtlPorts,
498 SmallVectorImpl<hw::PortInfo> &ports,
499 Operation *moduleOp, StringRef moduleName,
500 CircuitLoweringState &loweringState);
501 bool handleForceNameAnnos(FModuleLike oldModule,
AnnotationSet &annos,
502 CircuitLoweringState &loweringState);
503 hw::HWModuleOp lowerModule(FModuleOp oldModule, Block *topLevelModule,
504 CircuitLoweringState &loweringState);
505 hw::HWModuleExternOp lowerExtModule(FExtModuleOp oldModule,
506 Block *topLevelModule,
507 CircuitLoweringState &loweringState);
508 hw::HWModuleExternOp lowerMemModule(FMemModuleOp oldModule,
509 Block *topLevelModule,
510 CircuitLoweringState &loweringState);
513 lowerModulePortsAndMoveBody(FModuleOp oldModule, hw::HWModuleOp newModule,
514 CircuitLoweringState &loweringState);
515 LogicalResult lowerModuleOperations(hw::HWModuleOp module,
516 CircuitLoweringState &loweringState);
523 bool enableAnnotationWarning,
bool emitChiselAssertsAsSVA,
524 bool disableMemRandomization,
bool disableRegRandomization) {
525 auto pass = std::make_unique<FIRRTLModuleLowering>();
526 if (enableAnnotationWarning)
527 pass->setEnableAnnotationWarning();
528 if (emitChiselAssertsAsSVA)
529 pass->setEmitChiselAssertAsSVA();
530 if (disableMemRandomization)
531 pass->setDisableMemRandomization();
532 if (disableRegRandomization)
533 pass->setDisableRegRandomization();
539 void FIRRTLModuleLowering::runOnOperation() {
543 auto *topLevelModule = getOperation().getBody();
547 for (
auto &op : *topLevelModule) {
548 if ((circuit = dyn_cast<CircuitOp>(&op)))
555 auto *circuitBody = circuit.getBodyBlock();
559 CircuitLoweringState state(
560 circuit, enableAnnotationWarning, emitChiselAssertsAsSVA,
561 &getAnalysis<InstanceGraph>(), &getAnalysis<NLATable>());
563 SmallVector<hw::HWModuleOp, 32> modulesToProcess;
567 "firrtl.extract.assert");
569 "firrtl.extract.assume");
571 "firrtl.extract.cover");
572 circuitAnno.removeAnnotationsWithClass(
575 state.processRemainingAnnotations(circuit, circuitAnno);
578 for (
auto &op : make_early_inc_range(circuitBody->getOperations())) {
580 TypeSwitch<Operation *, LogicalResult>(&op)
581 .Case<FModuleOp>([&](
auto module) {
582 auto loweredMod = lowerModule(module, topLevelModule, state);
586 state.oldToNewModuleMap[&op] = loweredMod;
587 modulesToProcess.push_back(loweredMod);
589 module.walk([&](Operation *op) {
590 for (
auto res : op->getResults()) {
592 type_dyn_cast<BaseTypeAliasType>(res.getType()))
593 state.lowerType(aliasType, op->getLoc());
596 return lowerModulePortsAndMoveBody(module, loweredMod, state);
598 .Case<FExtModuleOp>([&](
auto extModule) {
600 lowerExtModule(extModule, topLevelModule, state);
603 state.oldToNewModuleMap[&op] = loweredMod;
606 .Case<FMemModuleOp>([&](
auto memModule) {
608 lowerMemModule(memModule, topLevelModule, state);
611 state.oldToNewModuleMap[&op] = loweredMod;
614 .Default([&](Operation *op) {
619 op->moveBefore(topLevelModule, topLevelModule->end());
625 return signalPassFailure();
628 state.typeAliases.freeze();
633 SmallVector<Attribute> dutHierarchyFiles;
634 SmallVector<Attribute> testHarnessHierarchyFiles;
635 circuitAnno.removeAnnotations([&](
Annotation annotation) {
637 auto file = hw::OutputFileAttr::getFromFilename(
639 annotation.
getMember<StringAttr>(
"filename").getValue(),
641 dutHierarchyFiles.push_back(file);
645 auto file = hw::OutputFileAttr::getFromFilename(
647 annotation.
getMember<StringAttr>(
"filename").getValue(),
651 if (state.getTestHarness())
652 testHarnessHierarchyFiles.push_back(file);
654 dutHierarchyFiles.push_back(file);
660 if (!dutHierarchyFiles.empty())
661 state.oldToNewModuleMap[state.getDut()]->setAttr(
664 if (!testHarnessHierarchyFiles.empty())
665 state.oldToNewModuleMap[state.getTestHarness()]->setAttr(
670 auto result = mlir::failableParallelForEachN(
671 &getContext(), 0, modulesToProcess.size(), [&](
auto index) {
672 return lowerModuleOperations(modulesToProcess[index], state);
677 return signalPassFailure();
680 for (
auto bind : state.binds) {
681 bind->moveBefore(bind->getParentOfType<hw::HWModuleOp>());
685 for (
auto oldNew : state.oldToNewModuleMap)
686 oldNew.first->erase();
689 lowerFileHeader(circuit, state);
696 void FIRRTLModuleLowering::lowerFileHeader(CircuitOp op,
697 CircuitLoweringState &state) {
702 StringSet<> emittedDecls;
704 auto emitDecl = [&](StringRef name, ArrayAttr args) {
705 if (emittedDecls.count(name))
707 emittedDecls.insert(name);
708 OpBuilder::InsertionGuard guard(b);
709 b.setInsertionPointAfter(op);
710 b.create<sv::MacroDeclOp>(name, args, StringAttr());
715 auto emitDefine = [&](StringRef name, StringRef body, ArrayAttr args = {}) {
716 emitDecl(name, args);
717 b.create<sv::MacroDefOp>(name, body);
722 auto emitGuardedDefine = [&](StringRef guard, StringRef defName,
723 StringRef defineTrue =
"",
724 StringRef defineFalse = StringRef()) {
725 if (!defineFalse.data()) {
726 assert(defineTrue.data() &&
"didn't define anything");
727 b.create<sv::IfDefOp>(guard, [&]() { emitDefine(defName, defineTrue); });
729 b.create<sv::IfDefOp>(
732 if (defineTrue.data())
733 emitDefine(defName, defineTrue);
735 [&]() { emitDefine(defName, defineFalse); });
740 auto emitGuard = [&](
const char *guard, llvm::function_ref<void(
void)> body) {
741 b.create<sv::IfDefOp>(
742 guard, []() {}, body);
745 bool needsRandomizeRegInit =
746 state.used_RANDOMIZE_REG_INIT && !disableRegRandomization;
747 bool needsRandomizeMemInit =
748 state.used_RANDOMIZE_MEM_INIT && !disableMemRandomization;
752 if (!state.used_RANDOMIZE_GARBAGE_ASSIGN && !needsRandomizeRegInit &&
753 !needsRandomizeMemInit && !state.used_PRINTF_COND &&
754 !state.used_ASSERT_VERBOSE_COND && !state.used_STOP_COND)
757 b.create<sv::VerbatimOp>(
758 "// Standard header to adapt well known macros to our needs.");
760 bool needRandom =
false;
761 if (state.used_RANDOMIZE_GARBAGE_ASSIGN) {
762 emitGuard(
"RANDOMIZE", [&]() {
763 emitGuardedDefine(
"RANDOMIZE_GARBAGE_ASSIGN",
"RANDOMIZE");
767 if (needsRandomizeRegInit) {
768 emitGuard(
"RANDOMIZE",
769 [&]() { emitGuardedDefine(
"RANDOMIZE_REG_INIT",
"RANDOMIZE"); });
772 if (needsRandomizeMemInit) {
773 emitGuard(
"RANDOMIZE",
774 [&]() { emitGuardedDefine(
"RANDOMIZE_MEM_INIT",
"RANDOMIZE"); });
779 b.create<sv::VerbatimOp>(
780 "\n// RANDOM may be set to an expression that produces a 32-bit "
781 "random unsigned value.");
782 emitGuardedDefine(
"RANDOM",
"RANDOM", StringRef(),
"$random");
785 if (state.used_PRINTF_COND) {
786 b.create<sv::VerbatimOp>(
787 "\n// Users can define 'PRINTF_COND' to add an extra gate to "
789 emitGuard(
"PRINTF_COND_", [&]() {
790 emitGuardedDefine(
"PRINTF_COND",
"PRINTF_COND_",
"(`PRINTF_COND)",
"1");
794 if (state.used_ASSERT_VERBOSE_COND) {
795 b.create<sv::VerbatimOp>(
796 "\n// Users can define 'ASSERT_VERBOSE_COND' to add an extra "
797 "gate to assert error printing.");
798 emitGuard(
"ASSERT_VERBOSE_COND_", [&]() {
799 emitGuardedDefine(
"ASSERT_VERBOSE_COND",
"ASSERT_VERBOSE_COND_",
800 "(`ASSERT_VERBOSE_COND)",
"1");
804 if (state.used_STOP_COND) {
805 b.create<sv::VerbatimOp>(
806 "\n// Users can define 'STOP_COND' to add an extra gate "
807 "to stop conditions.");
808 emitGuard(
"STOP_COND_", [&]() {
809 emitGuardedDefine(
"STOP_COND",
"STOP_COND_",
"(`STOP_COND)",
"1");
814 b.create<sv::VerbatimOp>(
815 "\n// Users can define INIT_RANDOM as general code that gets "
817 "into the\n// initializer block for modules with registers.");
818 emitGuardedDefine(
"INIT_RANDOM",
"INIT_RANDOM", StringRef(),
"");
820 b.create<sv::VerbatimOp>(
821 "\n// If using random initialization, you can also define "
822 "RANDOMIZE_DELAY to\n// customize the delay used, otherwise 0.002 "
824 emitGuardedDefine(
"RANDOMIZE_DELAY",
"RANDOMIZE_DELAY", StringRef(),
827 b.create<sv::VerbatimOp>(
828 "\n// Define INIT_RANDOM_PROLOG_ for use in our modules below.");
829 emitGuard(
"INIT_RANDOM_PROLOG_", [&]() {
830 b.create<sv::IfDefOp>(
833 emitGuardedDefine(
"VERILATOR",
"INIT_RANDOM_PROLOG_",
835 "`INIT_RANDOM #`RANDOMIZE_DELAY begin end");
837 [&]() { emitDefine(
"INIT_RANDOM_PROLOG_",
""); });
840 b.create<sv::VerbatimOp>(
"\n// Include register initializers in init "
841 "blocks unless synthesis is set");
842 emitGuard(
"SYNTHESIS", [&] {
843 emitGuardedDefine(
"ENABLE_INITIAL_REG_",
"ENABLE_INITIAL_REG_",
847 b.create<sv::VerbatimOp>(
"\n// Include rmemory initializers in init "
848 "blocks unless synthesis is set");
849 emitGuard(
"SYNTHESIS", [&] {
850 emitGuardedDefine(
"ENABLE_INITIAL_MEM_",
"ENABLE_INITIAL_MEM_",
855 if (state.used_RANDOMIZE_GARBAGE_ASSIGN) {
856 b.create<sv::VerbatimOp>(
857 "\n// RANDOMIZE_GARBAGE_ASSIGN enable range checks for mem "
859 emitGuard(
"RANDOMIZE_GARBAGE_ASSIGN_BOUND_CHECK", [&]() {
860 b.create<sv::IfDefOp>(
861 "RANDOMIZE_GARBAGE_ASSIGN",
863 StringRef args[] = {
"INDEX",
"VALUE",
"SIZE"};
864 emitDefine(
"RANDOMIZE_GARBAGE_ASSIGN_BOUND_CHECK",
865 " ((INDEX) < (SIZE) ? (VALUE) : {`RANDOM})",
866 b.getStrArrayAttr(ArrayRef(args)));
869 StringRef args[] = {
"INDEX",
"VALUE",
"SIZE"};
870 emitDefine(
"RANDOMIZE_GARBAGE_ASSIGN_BOUND_CHECK",
"(VALUE)",
871 b.getStrArrayAttr(args));
877 b.create<sv::VerbatimOp>(
"");
881 FIRRTLModuleLowering::lowerPorts(ArrayRef<PortInfo> firrtlPorts,
882 SmallVectorImpl<hw::PortInfo> &ports,
883 Operation *moduleOp, StringRef moduleName,
884 CircuitLoweringState &loweringState) {
885 ports.reserve(firrtlPorts.size());
887 size_t numResults = 0;
888 for (
auto firrtlPort : firrtlPorts) {
890 hwPort.name = firrtlPort.name;
891 hwPort.type = loweringState.lowerType(firrtlPort.type, firrtlPort.loc);
893 if (firrtlPort.sym.size() > 1 ||
894 (firrtlPort.sym.size() == 1 && !firrtlPort.sym.getSymName()))
895 return emitError(firrtlPort.loc)
896 <<
"cannot lower aggregate port " << firrtlPort.name
897 <<
" with field sensitive symbols, HW dialect does not support "
898 "per field symbols yet.";
899 hwPort.sym = firrtlPort.sym;
900 bool hadDontTouch = firrtlPort.annotations.removeDontTouch();
901 if (hadDontTouch && !hwPort.sym) {
902 if (hwPort.type.isInteger(0)) {
903 if (enableAnnotationWarning) {
904 mlir::emitWarning(firrtlPort.loc)
905 <<
"zero width port " << hwPort.name
906 <<
" has dontTouch annotation, removing anyway";
911 moduleOp->getContext(),
912 Twine(
"__") + moduleName + Twine(
"__") + firrtlPort.name.strref()));
917 moduleOp->emitError(
"cannot lower this port type to HW");
923 if (hwPort.type.isInteger(0)) {
924 if (hwPort.sym && !hwPort.sym.empty()) {
925 return mlir::emitError(firrtlPort.loc)
926 <<
"zero width port " << hwPort.name
927 <<
" is referenced by name [" << hwPort.sym
928 <<
"] (e.g. in an XMR) but must be removed";
934 if (firrtlPort.isOutput()) {
936 hwPort.argNum = numResults++;
937 }
else if (firrtlPort.isInput()) {
939 hwPort.argNum = numArgs++;
945 hwPort.argNum = numArgs++;
947 hwPort.loc = firrtlPort.loc;
948 ports.push_back(hwPort);
949 loweringState.processRemainingAnnotations(moduleOp, firrtlPort.annotations);
958 auto params = llvm::map_range(module.getParameters(), [](Attribute a) {
959 return cast<ParamDeclAttr>(a);
969 SmallVector<Attribute> newParams;
970 for (
const ParamDeclAttr &entry : params) {
971 auto name = entry.getName();
972 auto type = entry.getType();
973 auto value = ignoreValues ? Attribute() : entry.getValue();
976 newParams.push_back(paramAttr);
978 return builder.getArrayAttr(newParams);
981 bool FIRRTLModuleLowering::handleForceNameAnnos(
983 CircuitLoweringState &loweringState) {
990 auto sym = anno.
getMember<FlatSymbolRefAttr>(
"circt.nonlocal");
997 auto diag = oldModule.emitOpError()
999 <<
"' that is not a non-local annotation";
1000 diag.attachNote() <<
"the erroneous annotation is '" << anno.
getDict()
1006 auto nla = loweringState.nlaTable->getNLA(sym.getAttr());
1011 auto diag = oldModule.emitOpError()
1013 <<
"' whose non-local symbol, '" << sym
1014 <<
"' does not exist in the circuit";
1015 diag.attachNote() <<
"the erroneous annotation is '" << anno.
getDict();
1028 cast<hw::InnerRefAttr>(nla.getNamepath().getValue().take_back(2)[0]);
1029 auto inserted = loweringState.instanceForceNames.insert(
1030 {{inst.getModule(), inst.getName()}, anno.
getMember(
"name")});
1031 if (!inserted.second &&
1032 (anno.
getMember(
"name") != (inserted.first->second))) {
1033 auto diag = oldModule.emitError()
1035 <<
"' with different names: " << inserted.first->second
1036 <<
" was not " << anno.
getMember(
"name");
1037 diag.attachNote() <<
"the erroneous annotation is '" << anno.
getDict()
1047 hw::HWModuleExternOp
1048 FIRRTLModuleLowering::lowerExtModule(FExtModuleOp oldModule,
1049 Block *topLevelModule,
1050 CircuitLoweringState &loweringState) {
1052 SmallVector<PortInfo> firrtlPorts = oldModule.getPorts();
1053 SmallVector<hw::PortInfo, 8> ports;
1054 if (failed(lowerPorts(firrtlPorts, ports, oldModule, oldModule.getName(),
1058 StringRef verilogName;
1059 if (
auto defName = oldModule.getDefname())
1060 verilogName = defName.value();
1063 auto builder = OpBuilder::atBlockEnd(topLevelModule);
1064 auto nameAttr =
builder.getStringAttr(oldModule.getName());
1069 auto newModule =
builder.create<hw::HWModuleExternOp>(
1070 oldModule.getLoc(), nameAttr, ports, verilogName, parameters);
1071 SymbolTable::setSymbolVisibility(newModule,
1072 SymbolTable::getSymbolVisibility(oldModule));
1074 bool hasOutputPort =
1075 llvm::any_of(firrtlPorts, [&](
auto p) {
return p.isOutput(); });
1076 if (!hasOutputPort &&
1078 loweringState.isInDUT(oldModule))
1079 newModule->setAttr(
"firrtl.extract.cover.extra",
builder.getUnitAttr());
1082 if (handleForceNameAnnos(oldModule, annos, loweringState))
1085 loweringState.processRemainingAnnotations(oldModule, annos);
1089 hw::HWModuleExternOp
1090 FIRRTLModuleLowering::lowerMemModule(FMemModuleOp oldModule,
1091 Block *topLevelModule,
1092 CircuitLoweringState &loweringState) {
1094 SmallVector<PortInfo> firrtlPorts = oldModule.getPorts();
1095 SmallVector<hw::PortInfo, 8> ports;
1096 if (failed(lowerPorts(firrtlPorts, ports, oldModule, oldModule.getName(),
1101 auto builder = OpBuilder::atBlockEnd(topLevelModule);
1102 auto newModule =
builder.create<hw::HWModuleExternOp>(
1103 oldModule.getLoc(), oldModule.getModuleNameAttr(), ports,
1104 oldModule.getModuleNameAttr());
1105 loweringState.processRemainingAnnotations(oldModule,
1112 FIRRTLModuleLowering::lowerModule(FModuleOp oldModule, Block *topLevelModule,
1113 CircuitLoweringState &loweringState) {
1115 SmallVector<PortInfo> firrtlPorts = oldModule.getPorts();
1116 SmallVector<hw::PortInfo, 8> ports;
1117 if (failed(lowerPorts(firrtlPorts, ports, oldModule, oldModule.getName(),
1122 auto builder = OpBuilder::atBlockEnd(topLevelModule);
1123 auto nameAttr =
builder.getStringAttr(oldModule.getName());
1125 builder.create<hw::HWModuleOp>(oldModule.getLoc(), nameAttr, ports);
1126 if (
auto outputFile = oldModule->getAttr(
"output_file"))
1127 newModule->setAttr(
"output_file", outputFile);
1128 if (
auto comment = oldModule->getAttrOfType<StringAttr>(
"comment"))
1129 newModule.setCommentAttr(comment);
1136 if (
auto randomWidth = oldModule->getAttr(
"firrtl.random_init_width"))
1137 newModule->setAttr(
"firrtl.random_init_width", randomWidth);
1141 SymbolTable::setSymbolVisibility(newModule,
1142 SymbolTable::getSymbolVisibility(oldModule));
1148 newModule->setAttr(
"firrtl.extract.cover.extra",
builder.getUnitAttr());
1151 if (
auto testBenchDir = loweringState.getTestBenchDirectory())
1152 if (loweringState.isInTestHarness(oldModule)) {
1153 newModule->setAttr(
"output_file", testBenchDir);
1154 newModule->setAttr(
"firrtl.extract.do_not_extract",
1156 newModule.setCommentAttr(
1157 builder.getStringAttr(
"VCS coverage exclude_file"));
1160 if (handleForceNameAnnos(oldModule, annos, loweringState))
1163 loweringState.processRemainingAnnotations(oldModule, annos);
1172 Operation *insertPoint) {
1173 if (!value.hasOneUse())
1176 auto attach = dyn_cast<AttachOp>(*value.user_begin());
1177 if (!attach || attach.getNumOperands() != 2)
1181 auto loweredType =
lowerType(value.getType());
1182 if (loweredType.isInteger(0))
1187 auto attachedValue = attach.getOperand(attach.getOperand(0) == value);
1188 auto *op = attachedValue.getDefiningOp();
1189 if (op && op->getBlock() == insertPoint->getBlock() &&
1190 !op->isBeforeInBlock(insertPoint))
1195 ImplicitLocOpBuilder
builder(insertPoint->getLoc(), insertPoint);
1209 CircuitLoweringState &loweringState) {
1211 if (type_isa<AnalogType>(flipValue.getType()))
1214 Operation *connectOp =
nullptr;
1215 for (
auto &use : flipValue.getUses()) {
1218 if (use.getOperandNumber() != 0)
1220 if (!isa<ConnectOp, StrictConnectOp>(use.getOwner()))
1226 connectOp = use.getOwner();
1236 loweringState.lowerType(flipValue.getType(), flipValue.getLoc());
1237 if (loweredType.isInteger(0))
1242 ImplicitLocOpBuilder
builder(insertPoint->getLoc(), insertPoint);
1244 auto connectSrc = connectOp->getOperand(1);
1247 if (!connectSrc.getType().isa<
FIRRTLType>()) {
1253 if (!type_cast<FIRRTLBaseType>(connectSrc.getType()).isPassive())
1255 .create<mlir::UnrealizedConversionCastOp>(
1256 type_cast<FIRRTLBaseType>(connectSrc.getType())
1263 auto destTy = type_cast<FIRRTLBaseType>(flipValue.getType()).getPassiveType();
1265 if (destTy != connectSrc.getType() &&
1266 (isa<BaseTypeAliasType>(connectSrc.getType()) ||
1267 isa<BaseTypeAliasType>(destTy))) {
1269 builder.createOrFold<BitCastOp>(flipValue.getType(), connectSrc);
1271 if (!destTy.isGround()) {
1273 if (destTy != type_cast<FIRRTLType>(connectSrc.getType()))
1275 }
else if (destTy.getBitWidthOrSentinel() !=
1276 type_cast<FIRRTLBaseType>(connectSrc.getType())
1277 .getBitWidthOrSentinel()) {
1280 auto destWidth = destTy.getBitWidthOrSentinel();
1281 assert(destWidth != -1 &&
"must know integer widths");
1282 connectSrc =
builder.createOrFold<PadPrimOp>(destTy, connectSrc, destWidth);
1294 SmallVector<SubfieldOp> accesses;
1295 for (
auto *op : structValue.getUsers()) {
1296 assert(isa<SubfieldOp>(op));
1297 auto fieldAccess = cast<SubfieldOp>(op);
1299 fieldAccess.getInput().getType().get().getElementIndex(field);
1300 if (elemIndex && *elemIndex == fieldAccess.getFieldIndex())
1301 accesses.push_back(fieldAccess);
1309 LogicalResult FIRRTLModuleLowering::lowerModulePortsAndMoveBody(
1310 FModuleOp oldModule, hw::HWModuleOp newModule,
1311 CircuitLoweringState &loweringState) {
1312 ImplicitLocOpBuilder bodyBuilder(oldModule.getLoc(), newModule.getBody());
1318 auto cursor = bodyBuilder.create<hw::ConstantOp>(APInt(1, 1));
1319 bodyBuilder.setInsertionPoint(cursor);
1322 SmallVector<PortInfo> ports = oldModule.getPorts();
1323 assert(oldModule.getBody().getNumArguments() == ports.size() &&
1324 "port count mismatch");
1326 size_t nextNewArg = 0;
1327 size_t firrtlArg = 0;
1328 SmallVector<Value, 4>
outputs;
1331 auto outputOp = newModule.getBodyBlock()->getTerminator();
1332 ImplicitLocOpBuilder outputBuilder(oldModule.getLoc(), outputOp);
1334 for (
auto &port : ports) {
1336 auto oldArg = oldModule.getBody().getArgument(firrtlArg++);
1339 type_isa<FIRRTLBaseType>(port.type) &&
1340 type_cast<FIRRTLBaseType>(port.type).getBitWidthOrSentinel() == 0;
1342 if (!port.isOutput() && !isZeroWidth) {
1345 Value newArg = newModule.getBody().getArgument(nextNewArg++);
1351 oldArg.replaceAllUsesWith(newArg);
1357 if (isZeroWidth && port.isInput()) {
1358 Value newArg = bodyBuilder
1359 .create<WireOp>(port.type,
"." + port.getName().str() +
1362 oldArg.replaceAllUsesWith(newArg);
1371 assert(oldArg.use_empty() &&
"should have removed all uses of oldArg");
1377 auto newArg = bodyBuilder.create<WireOp>(
1378 port.type,
"." + port.getName().str() +
".output");
1381 oldArg.replaceAllUsesWith(newArg.getResult());
1384 auto resultHWType = loweringState.lowerType(port.type, port.loc);
1385 if (!resultHWType.isInteger(0)) {
1388 auto idx = newModule.getNumInputPorts() +
outputs.size();
1392 if (
auto sym = newModule.getPortSymbolAttr(idx)) {
1393 newArg.setInnerSymAttr(sym);
1394 newModule.setPortSymbolAttr(idx, {});
1400 outputOp->setOperands(
outputs);
1403 auto &oldBlockInstList = oldModule.getBodyBlock()->getOperations();
1404 auto &newBlockInstList = newModule.getBodyBlock()->getOperations();
1405 newBlockInstList.splice(Block::iterator(cursor), oldBlockInstList,
1406 oldBlockInstList.begin(), oldBlockInstList.end());
1420 struct FIRRTLLowering :
public FIRRTLVisitor<FIRRTLLowering, LogicalResult> {
1422 FIRRTLLowering(hw::HWModuleOp module, CircuitLoweringState &circuitState)
1423 : theModule(module), circuitState(circuitState),
1424 builder(module.getLoc(), module.getContext()), moduleNamespace(module),
1425 backedgeBuilder(
builder, module.getLoc()) {}
1427 LogicalResult run();
1430 Value getOrCreateClockConstant(seq::ClockConst clock);
1431 Value getOrCreateIntConstant(
const APInt &value);
1432 Value getOrCreateIntConstant(
unsigned numBits, uint64_t val,
1433 bool isSigned =
false) {
1434 return getOrCreateIntConstant(APInt(numBits, val, isSigned));
1436 Attribute getOrCreateAggregateConstantAttribute(Attribute value, Type type);
1437 Value getOrCreateXConstant(
unsigned numBits);
1438 Value getOrCreateZConstant(Type type);
1439 Value getPossiblyInoutLoweredValue(Value value);
1440 Value getLoweredValue(Value value);
1441 Value getLoweredNonClockValue(Value value);
1442 Value getLoweredAndExtendedValue(Value value, Type destType);
1443 Value getLoweredAndExtOrTruncValue(Value value, Type destType);
1444 LogicalResult setLowering(Value orig, Value result);
1445 LogicalResult setPossiblyFoldedLowering(Value orig, Value result);
1446 template <
typename ResultOpType,
typename... CtorArgTypes>
1447 LogicalResult setLoweringTo(Operation *orig, CtorArgTypes... args);
1448 template <
typename ResultOpType,
typename... CtorArgTypes>
1449 LogicalResult setLoweringToLTL(Operation *orig, CtorArgTypes... args);
1450 Backedge createBackedge(Location loc, Type type);
1451 Backedge createBackedge(Value orig, Type type);
1452 bool updateIfBackedge(Value dest, Value src);
1455 bool requiresInnerSymbol(hw::InnerSymbolOpInterface op) {
1460 if (
auto forceable = dyn_cast<Forceable>(op.getOperation()))
1461 if (forceable.isForceable())
1469 hw::InnerSymAttr lowerInnerSymbol(hw::InnerSymbolOpInterface op) {
1470 auto attr = op.getInnerSymAttr();
1474 if (requiresInnerSymbol(op))
1476 op.getContext(), attr, 0,
1477 [&]() -> hw::InnerSymbolNamespace & { return moduleNamespace; });
1481 void runWithInsertionPointAtEndOfBlock(std::function<
void(
void)> fn,
1485 Value getReadValue(Value v);
1487 Value getNonClockValue(Value v);
1489 void addToAlwaysBlock(sv::EventControl clockEdge, Value clock,
1490 ::ResetType resetStyle, sv::EventControl resetEdge,
1491 Value reset, std::function<
void(
void)> body = {},
1492 std::function<void(
void)> resetBody = {});
1493 void addToAlwaysBlock(Value clock, std::function<
void(
void)> body = {}) {
1494 addToAlwaysBlock(sv::EventControl::AtPosEdge, clock, ::ResetType(),
1495 sv::EventControl(), Value(), body,
1496 std::function<
void(
void)>());
1499 void addToIfDefBlock(StringRef cond, std::function<
void(
void)> thenCtor,
1500 std::function<
void(
void)> elseCtor = {});
1501 void addToInitialBlock(std::function<
void(
void)> body);
1502 void addIfProceduralBlock(Value cond, std::function<
void(
void)> thenCtor,
1503 std::function<
void(
void)> elseCtor = {});
1504 Value getExtOrTruncAggregateValue(Value array,
FIRRTLBaseType sourceType,
1506 bool allowTruncate);
1507 Value createArrayIndexing(Value array, Value index);
1508 Value createValueWithMuxAnnotation(Operation *op,
bool isMux2);
1515 enum UnloweredOpResult { AlreadyLowered, NowLowered, LoweringFailure };
1516 UnloweredOpResult handleUnloweredOp(Operation *op);
1517 LogicalResult visitExpr(ConstantOp op);
1518 LogicalResult visitExpr(SpecialConstantOp op);
1519 LogicalResult visitExpr(SubindexOp op);
1520 LogicalResult visitExpr(SubaccessOp op);
1521 LogicalResult visitExpr(SubfieldOp op);
1522 LogicalResult visitExpr(VectorCreateOp op);
1523 LogicalResult visitExpr(BundleCreateOp op);
1524 LogicalResult visitExpr(FEnumCreateOp op);
1525 LogicalResult visitExpr(AggregateConstantOp op);
1526 LogicalResult visitExpr(IsTagOp op);
1527 LogicalResult visitExpr(SubtagOp op);
1528 LogicalResult visitUnhandledOp(Operation *op) {
return failure(); }
1529 LogicalResult visitInvalidOp(Operation *op) {
return failure(); }
1532 LogicalResult visitDecl(WireOp op);
1533 LogicalResult visitDecl(NodeOp op);
1534 LogicalResult visitDecl(RegOp op);
1535 LogicalResult visitDecl(RegResetOp op);
1536 LogicalResult visitDecl(MemOp op);
1537 LogicalResult visitDecl(InstanceOp op);
1538 LogicalResult visitDecl(VerbatimWireOp op);
1541 LogicalResult lowerNoopCast(Operation *op);
1542 LogicalResult visitExpr(AsSIntPrimOp op);
1543 LogicalResult visitExpr(AsUIntPrimOp op);
1544 LogicalResult visitExpr(AsClockPrimOp op);
1545 LogicalResult visitExpr(AsAsyncResetPrimOp op) {
return lowerNoopCast(op); }
1547 LogicalResult visitExpr(HWStructCastOp op);
1548 LogicalResult visitExpr(BitCastOp op);
1549 LogicalResult visitExpr(mlir::UnrealizedConversionCastOp op);
1550 LogicalResult visitExpr(CvtPrimOp op);
1551 LogicalResult visitExpr(NotPrimOp op);
1552 LogicalResult visitExpr(NegPrimOp op);
1553 LogicalResult visitExpr(PadPrimOp op);
1554 LogicalResult visitExpr(XorRPrimOp op);
1555 LogicalResult visitExpr(AndRPrimOp op);
1556 LogicalResult visitExpr(OrRPrimOp op);
1559 template <
typename ResultUnsignedOpType,
1560 typename ResultSignedOpType = ResultUnsignedOpType>
1561 LogicalResult lowerBinOp(Operation *op);
1562 template <
typename ResultOpType>
1563 LogicalResult lowerBinOpToVariadic(Operation *op);
1565 template <
typename ResultOpType>
1566 LogicalResult lowerElementwiseLogicalOp(Operation *op);
1568 LogicalResult lowerCmpOp(Operation *op, ICmpPredicate signedOp,
1569 ICmpPredicate unsignedOp);
1570 template <
typename SignedOp,
typename Un
signedOp>
1571 LogicalResult lowerDivLikeOp(Operation *op);
1573 LogicalResult visitExpr(CatPrimOp op);
1575 LogicalResult visitExpr(AndPrimOp op) {
1576 return lowerBinOpToVariadic<comb::AndOp>(op);
1578 LogicalResult visitExpr(OrPrimOp op) {
1579 return lowerBinOpToVariadic<comb::OrOp>(op);
1581 LogicalResult visitExpr(XorPrimOp op) {
1582 return lowerBinOpToVariadic<comb::XorOp>(op);
1584 LogicalResult visitExpr(ElementwiseOrPrimOp op) {
1585 return lowerElementwiseLogicalOp<comb::OrOp>(op);
1587 LogicalResult visitExpr(ElementwiseAndPrimOp op) {
1588 return lowerElementwiseLogicalOp<comb::AndOp>(op);
1590 LogicalResult visitExpr(ElementwiseXorPrimOp op) {
1591 return lowerElementwiseLogicalOp<comb::XorOp>(op);
1593 LogicalResult visitExpr(AddPrimOp op) {
1594 return lowerBinOpToVariadic<comb::AddOp>(op);
1596 LogicalResult visitExpr(EQPrimOp op) {
1597 return lowerCmpOp(op, ICmpPredicate::eq, ICmpPredicate::eq);
1599 LogicalResult visitExpr(NEQPrimOp op) {
1600 return lowerCmpOp(op, ICmpPredicate::ne, ICmpPredicate::ne);
1602 LogicalResult visitExpr(LTPrimOp op) {
1603 return lowerCmpOp(op, ICmpPredicate::slt, ICmpPredicate::ult);
1605 LogicalResult visitExpr(LEQPrimOp op) {
1606 return lowerCmpOp(op, ICmpPredicate::sle, ICmpPredicate::ule);
1608 LogicalResult visitExpr(GTPrimOp op) {
1609 return lowerCmpOp(op, ICmpPredicate::sgt, ICmpPredicate::ugt);
1611 LogicalResult visitExpr(GEQPrimOp op) {
1612 return lowerCmpOp(op, ICmpPredicate::sge, ICmpPredicate::uge);
1615 LogicalResult visitExpr(SubPrimOp op) {
return lowerBinOp<comb::SubOp>(op); }
1616 LogicalResult visitExpr(MulPrimOp op) {
1617 return lowerBinOpToVariadic<comb::MulOp>(op);
1619 LogicalResult visitExpr(DivPrimOp op) {
1620 return lowerDivLikeOp<comb::DivSOp, comb::DivUOp>(op);
1622 LogicalResult visitExpr(RemPrimOp op) {
1623 return lowerDivLikeOp<comb::ModSOp, comb::ModUOp>(op);
1627 LogicalResult visitExpr(IsXIntrinsicOp op);
1628 LogicalResult visitExpr(PlusArgsTestIntrinsicOp op);
1629 LogicalResult visitExpr(PlusArgsValueIntrinsicOp op);
1630 LogicalResult visitExpr(SizeOfIntrinsicOp op);
1631 LogicalResult visitExpr(ClockGateIntrinsicOp op);
1632 LogicalResult visitExpr(LTLAndIntrinsicOp op);
1633 LogicalResult visitExpr(LTLOrIntrinsicOp op);
1634 LogicalResult visitExpr(LTLDelayIntrinsicOp op);
1635 LogicalResult visitExpr(LTLConcatIntrinsicOp op);
1636 LogicalResult visitExpr(LTLNotIntrinsicOp op);
1637 LogicalResult visitExpr(LTLImplicationIntrinsicOp op);
1638 LogicalResult visitExpr(LTLEventuallyIntrinsicOp op);
1639 LogicalResult visitExpr(LTLClockIntrinsicOp op);
1640 LogicalResult visitExpr(LTLDisableIntrinsicOp op);
1641 LogicalResult visitStmt(VerifAssertIntrinsicOp op);
1642 LogicalResult visitStmt(VerifAssumeIntrinsicOp op);
1643 LogicalResult visitStmt(VerifCoverIntrinsicOp op);
1644 LogicalResult visitExpr(HasBeenResetIntrinsicOp op);
1647 LogicalResult visitExpr(BitsPrimOp op);
1648 LogicalResult visitExpr(InvalidValueOp op);
1649 LogicalResult visitExpr(HeadPrimOp op);
1650 LogicalResult visitExpr(ShlPrimOp op);
1651 LogicalResult visitExpr(ShrPrimOp op);
1652 LogicalResult visitExpr(DShlPrimOp op) {
1653 return lowerDivLikeOp<comb::ShlOp, comb::ShlOp>(op);
1655 LogicalResult visitExpr(DShrPrimOp op) {
1656 return lowerDivLikeOp<comb::ShrSOp, comb::ShrUOp>(op);
1658 LogicalResult visitExpr(DShlwPrimOp op) {
1659 return lowerDivLikeOp<comb::ShlOp, comb::ShlOp>(op);
1661 LogicalResult visitExpr(TailPrimOp op);
1662 LogicalResult visitExpr(MuxPrimOp op);
1663 LogicalResult visitExpr(Mux2CellIntrinsicOp op);
1664 LogicalResult visitExpr(Mux4CellIntrinsicOp op);
1665 LogicalResult visitExpr(MultibitMuxOp op);
1666 LogicalResult visitExpr(VerbatimExprOp op);
1667 LogicalResult visitExpr(XMRRefOp op);
1668 LogicalResult visitExpr(XMRDerefOp op);
1671 LogicalResult lowerVerificationStatement(
1672 Operation *op, StringRef labelPrefix, Value clock, Value predicate,
1673 Value enable, StringAttr messageAttr, ValueRange operands,
1674 StringAttr nameAttr,
bool isConcurrent, EventControl eventControl);
1676 LogicalResult visitStmt(SkipOp op);
1679 LogicalResult visitStmt(ConnectOp op);
1680 LogicalResult visitStmt(StrictConnectOp op);
1681 LogicalResult visitStmt(ForceOp op);
1682 LogicalResult visitStmt(PrintFOp op);
1683 LogicalResult visitStmt(StopOp op);
1684 LogicalResult visitStmt(AssertOp op);
1685 LogicalResult visitStmt(AssumeOp op);
1686 LogicalResult visitStmt(CoverOp op);
1687 LogicalResult visitStmt(AttachOp op);
1688 LogicalResult visitStmt(RefForceOp op);
1689 LogicalResult visitStmt(RefForceInitialOp op);
1690 LogicalResult visitStmt(RefReleaseOp op);
1691 LogicalResult visitStmt(RefReleaseInitialOp op);
1697 LogicalResult fixupLTLOps();
1700 return circuitState.lowerType(type,
builder.getLoc());
1705 hw::HWModuleOp theModule;
1708 CircuitLoweringState &circuitState;
1716 DenseMap<Value, Value> valueMapping;
1720 DenseMap<Value, Value> fromClockMapping;
1724 DenseMap<Attribute, Value> hwConstantMap;
1725 DenseMap<std::pair<Attribute, Type>, Attribute> hwAggregateConstantMap;
1729 DenseMap<unsigned, Value> hwConstantXMap;
1730 DenseMap<Type, Value> hwConstantZMap;
1736 DenseMap<Value, Value> readInOutCreated;
1740 using AlwaysKeyType = std::tuple<
Block *, sv::EventControl, Value,
1741 ::ResetType, sv::EventControl, Value>;
1749 hw::InnerSymbolNamespace moduleNamespace;
1758 llvm::MapVector<Value, Value> backedges;
1765 DenseSet<Operation *> maybeUnusedValues;
1767 void maybeUnused(Operation *op) { maybeUnusedValues.insert(op); }
1768 void maybeUnused(Value value) {
1769 if (
auto *op = value.getDefiningOp())
1781 SetVector<Operation *> ltlOpFixupWorklist;
1785 LogicalResult FIRRTLModuleLowering::lowerModuleOperations(
1786 hw::HWModuleOp module, CircuitLoweringState &loweringState) {
1787 return FIRRTLLowering(module, loweringState).run();
1791 LogicalResult FIRRTLLowering::run() {
1795 auto &body = theModule.getBody();
1797 SmallVector<Operation *, 16> opsToRemove;
1801 for (
auto &op : body.front().getOperations()) {
1802 builder.setInsertionPoint(&op);
1804 auto done = succeeded(dispatchVisitor(&op));
1805 circuitState.processRemainingAnnotations(&op,
AnnotationSet(&op));
1807 opsToRemove.push_back(&op);
1809 switch (handleUnloweredOp(&op)) {
1810 case AlreadyLowered:
1813 opsToRemove.push_back(&op);
1815 case LoweringFailure:
1827 for (
auto &[backedge, value] : backedges) {
1828 SmallVector<Location> driverLocs;
1834 if (backedge == value) {
1835 Location edgeLoc = backedge.getLoc();
1836 if (driverLocs.empty()) {
1837 mlir::emitError(edgeLoc,
"sink does not have a driver");
1839 auto diag = mlir::emitError(edgeLoc,
"sink in combinational loop");
1840 for (
auto loc : driverLocs)
1841 diag.attachNote(loc) <<
"through driver here";
1847 auto it = backedges.find(value);
1848 if (it == backedges.end())
1851 driverLocs.push_back(value.getLoc());
1854 if (
auto *defOp = backedge.getDefiningOp())
1855 maybeUnusedValues.erase(defOp);
1856 backedge.replaceAllUsesWith(value);
1863 while (!opsToRemove.empty()) {
1864 assert(opsToRemove.back()->use_empty() &&
1865 "Should remove ops in reverse order of visitation");
1866 maybeUnusedValues.erase(opsToRemove.back());
1867 opsToRemove.pop_back_val()->erase();
1871 while (!maybeUnusedValues.empty()) {
1872 auto it = maybeUnusedValues.begin();
1874 maybeUnusedValues.erase(it);
1875 if (!isOpTriviallyDead(op))
1877 for (
auto operand : op->getOperands())
1878 if (
auto *defOp = operand.getDefiningOp())
1879 maybeUnusedValues.insert(defOp);
1885 if (failed(fixupLTLOps()))
1896 Value FIRRTLLowering::getOrCreateClockConstant(seq::ClockConst clock) {
1899 auto &entry = hwConstantMap[attr];
1903 OpBuilder entryBuilder(&theModule.getBodyBlock()->front());
1904 entry = entryBuilder.create<seq::ConstClockOp>(
builder.getLoc(), attr);
1910 Value FIRRTLLowering::getOrCreateIntConstant(
const APInt &value) {
1911 auto attr =
builder.getIntegerAttr(
1912 builder.getIntegerType(value.getBitWidth()), value);
1914 auto &entry = hwConstantMap[attr];
1918 OpBuilder entryBuilder(&theModule.getBodyBlock()->front());
1919 entry = entryBuilder.create<hw::ConstantOp>(
builder.getLoc(), attr);
1925 Attribute FIRRTLLowering::getOrCreateAggregateConstantAttribute(Attribute value,
1928 if (hw::type_isa<IntegerType>(type))
1929 return builder.getIntegerAttr(type, cast<IntegerAttr>(value).getValue());
1931 auto cache = hwAggregateConstantMap.lookup({value, type});
1936 SmallVector<Attribute> values;
1937 for (
auto e : llvm::enumerate(cast<ArrayAttr>(value))) {
1939 if (
auto array = hw::type_dyn_cast<hw::ArrayType>(type))
1940 subType = array.getElementType();
1941 else if (
auto structType = hw::type_dyn_cast<hw::StructType>(type))
1942 subType = structType.getElements()[e.index()].type;
1944 assert(
false &&
"type must be either array or struct");
1946 values.push_back(getOrCreateAggregateConstantAttribute(e.value(), subType));
1950 if (hw::type_isa<hw::ArrayType>(type))
1951 std::reverse(values.begin(), values.end());
1953 auto &entry = hwAggregateConstantMap[{value, type}];
1954 entry =
builder.getArrayAttr(values);
1962 std::function<LogicalResult()> fn) {
1963 assert(failedOperand &&
"Should be called on the failed operand");
1971 Value FIRRTLLowering::getOrCreateXConstant(
unsigned numBits) {
1973 auto &entry = hwConstantXMap[numBits];
1977 OpBuilder entryBuilder(&theModule.getBodyBlock()->front());
1978 entry = entryBuilder.create<sv::ConstantXOp>(
1979 builder.getLoc(), entryBuilder.getIntegerType(numBits));
1983 Value FIRRTLLowering::getOrCreateZConstant(Type type) {
1984 auto &entry = hwConstantZMap[type];
1986 OpBuilder entryBuilder(&theModule.getBodyBlock()->front());
1987 entry = entryBuilder.create<sv::ConstantZOp>(
builder.getLoc(), type);
1996 Value FIRRTLLowering::getPossiblyInoutLoweredValue(Value value) {
1998 if (value.isa<BlockArgument>())
2002 if (
auto lowering = valueMapping.lookup(value)) {
2004 "Lowered value should be a non-FIRRTL value");
2013 Value FIRRTLLowering::getLoweredValue(Value value) {
2014 auto result = getPossiblyInoutLoweredValue(value);
2021 return getReadValue(result);
2027 Value FIRRTLLowering::getLoweredNonClockValue(Value value) {
2028 auto result = getLoweredValue(value);
2032 if (hw::type_isa<seq::ClockType>(result.getType()))
2033 return getNonClockValue(result);
2041 Value FIRRTLLowering::getExtOrTruncAggregateValue(Value array,
2044 bool allowTruncate) {
2045 SmallVector<Value> resultBuffer;
2050 auto srcWidth = firrtl::type_cast<IntType>(sourceType).getWidthOrSentinel();
2051 auto destWidth = firrtl::type_cast<IntType>(destType).getWidthOrSentinel();
2052 auto resultType =
builder.getIntegerType(destWidth);
2054 if (srcWidth == destWidth)
2057 if (srcWidth > destWidth) {
2059 return builder.createOrFold<comb::ExtractOp>(resultType, value, 0);
2061 builder.emitError(
"operand should not be a truncation");
2065 if (firrtl::type_cast<IntType>(sourceType).isSigned())
2067 auto zero = getOrCreateIntConstant(destWidth - srcWidth, 0);
2068 return builder.createOrFold<comb::ConcatOp>(zero, value);
2075 return TypeSwitch<FIRRTLBaseType, LogicalResult>(srcType)
2076 .Case<FVectorType>([&](
auto srcVectorType) {
2077 auto destVectorType = firrtl::type_cast<FVectorType>(destType);
2078 unsigned size = resultBuffer.size();
2079 unsigned indexWidth =
2081 for (
size_t i = 0, e = std::min(srcVectorType.getNumElements(),
2082 destVectorType.getNumElements());
2084 auto iIdx = getOrCreateIntConstant(indexWidth, i);
2085 auto arrayIndex =
builder.create<hw::ArrayGetOp>(src, iIdx);
2086 if (failed(recurse(arrayIndex, srcVectorType.getElementType(),
2087 destVectorType.getElementType())))
2090 SmallVector<Value> temp(resultBuffer.begin() +
size,
2091 resultBuffer.end());
2092 auto array =
builder.createOrFold<hw::ArrayCreateOp>(temp);
2093 resultBuffer.resize(
size);
2094 resultBuffer.push_back(array);
2097 .Case<BundleType>([&](BundleType srcStructType) {
2098 auto destStructType = firrtl::type_cast<BundleType>(destType);
2099 unsigned size = resultBuffer.size();
2102 if (destStructType.getNumElements() != srcStructType.getNumElements())
2105 for (
auto elem : llvm::enumerate(destStructType)) {
2106 auto structExtract =
2107 builder.create<hw::StructExtractOp>(src, elem.value().name);
2108 if (failed(recurse(structExtract,
2109 srcStructType.getElementType(elem.index()),
2110 destStructType.getElementType(elem.index()))))
2113 SmallVector<Value> temp(resultBuffer.begin() +
size,
2114 resultBuffer.end());
2115 auto newStruct =
builder.createOrFold<hw::StructCreateOp>(
2117 resultBuffer.resize(
size);
2118 resultBuffer.push_back(newStruct);
2121 .Case<IntType>([&](
auto) {
2122 if (
auto result = cast(src, srcType, destType)) {
2123 resultBuffer.push_back(result);
2128 .Default([&](
auto) {
return failure(); });
2131 if (failed(recurse(array, sourceType, destType)))
2134 assert(resultBuffer.size() == 1 &&
2135 "resultBuffer must only contain a result array if `success` is true");
2136 return resultBuffer[0];
2144 Value FIRRTLLowering::getLoweredAndExtendedValue(Value value, Type destType) {
2145 assert(type_isa<FIRRTLBaseType>(value.getType()) &&
2146 type_isa<FIRRTLBaseType>(destType) &&
2147 "input/output value should be FIRRTL");
2150 auto destWidth = type_cast<FIRRTLBaseType>(destType).getBitWidthOrSentinel();
2151 if (destWidth == -1)
2154 auto result = getLoweredValue(value);
2166 return getOrCreateIntConstant(destWidth, 0);
2170 cast<FIRRTLBaseType>(value.getType()).getBitWidthOrSentinel()) {
2172 auto loweredDstType =
lowerType(destType);
2173 if (result.getType() != loweredDstType &&
2174 (isa<hw::TypeAliasType>(result.getType()) ||
2175 isa<hw::TypeAliasType>(loweredDstType))) {
2176 return builder.createOrFold<hw::BitcastOp>(loweredDstType, result);
2180 if (result.getType().isa<hw::ArrayType, hw::StructType>()) {
2182 if (destType == value.getType())
2185 return getExtOrTruncAggregateValue(
2186 result, type_cast<FIRRTLBaseType>(value.getType()),
2187 type_cast<FIRRTLBaseType>(destType),
2191 if (result.getType().isa<seq::ClockType>()) {
2193 if (destType == value.getType())
2195 builder.emitError(
"cannot use clock type as an integer");
2199 auto intResultType = dyn_cast<IntegerType>(result.getType());
2200 if (!intResultType) {
2201 builder.emitError(
"operand of type ")
2202 << result.getType() <<
" cannot be used as an integer";
2206 auto srcWidth = intResultType.getWidth();
2207 if (srcWidth ==
unsigned(destWidth))
2210 if (srcWidth >
unsigned(destWidth)) {
2211 builder.emitError(
"operand should not be a truncation");
2215 auto resultType =
builder.getIntegerType(destWidth);
2219 type_cast<FIRRTLBaseType>(value.getType()).getPassiveType();
2220 if (type_cast<IntType>(valueFIRType).isSigned())
2223 auto zero = getOrCreateIntConstant(destWidth - srcWidth, 0);
2224 return builder.createOrFold<comb::ConcatOp>(zero, result);
2232 Value FIRRTLLowering::getLoweredAndExtOrTruncValue(Value value, Type destType) {
2233 assert(type_isa<FIRRTLBaseType>(value.getType()) &&
2234 type_isa<FIRRTLBaseType>(destType) &&
2235 "input/output value should be FIRRTL");
2238 auto destWidth = type_cast<FIRRTLBaseType>(destType).getBitWidthOrSentinel();
2239 if (destWidth == -1)
2242 auto result = getLoweredValue(value);
2254 return getOrCreateIntConstant(destWidth, 0);
2258 if (result.getType().isa<hw::ArrayType, hw::StructType>()) {
2260 if (destType == value.getType())
2263 return getExtOrTruncAggregateValue(
2264 result, type_cast<FIRRTLBaseType>(value.getType()),
2265 type_cast<FIRRTLBaseType>(destType),
2269 auto srcWidth = type_cast<IntegerType>(result.getType()).getWidth();
2270 if (srcWidth ==
unsigned(destWidth))
2276 if (srcWidth >
unsigned(destWidth)) {
2277 auto resultType =
builder.getIntegerType(destWidth);
2278 return builder.createOrFold<comb::ExtractOp>(resultType, result, 0);
2281 auto resultType =
builder.getIntegerType(destWidth);
2285 type_cast<FIRRTLBaseType>(value.getType()).getPassiveType();
2286 if (type_cast<IntType>(valueFIRType).isSigned())
2289 auto zero = getOrCreateIntConstant(destWidth - srcWidth, 0);
2290 return builder.createOrFold<comb::ConcatOp>(zero, result);
2299 LogicalResult FIRRTLLowering::setLowering(Value orig, Value result) {
2300 if (
auto origType = dyn_cast<FIRRTLType>(orig.getType())) {
2301 assert((!result || !type_isa<FIRRTLType>(result.getType())) &&
2302 "Lowering didn't turn a FIRRTL value into a non-FIRRTL value");
2306 auto srcWidth = baseType.getPassiveType().getBitWidthOrSentinel();
2309 if (srcWidth != -1) {
2311 assert((srcWidth != 0) &&
2312 "Lowering produced value for zero width source");
2314 assert((srcWidth == 0) &&
2315 "Lowering produced null value but source wasn't zero width");
2319 assert(result &&
"Lowering of foreign type produced null value");
2322 auto &slot = valueMapping[orig];
2323 assert(!slot &&
"value lowered multiple times");
2330 LogicalResult FIRRTLLowering::setPossiblyFoldedLowering(Value orig,
2334 if (
auto cst = dyn_cast_or_null<hw::ConstantOp>(result.getDefiningOp())) {
2335 auto &entry = hwConstantMap[cst.getValueAttr()];
2346 cst->moveBefore(&theModule.getBodyBlock()->front());
2350 return setLowering(orig, result);
2355 template <
typename ResultOpType,
typename... CtorArgTypes>
2356 LogicalResult FIRRTLLowering::setLoweringTo(Operation *orig,
2357 CtorArgTypes... args) {
2358 auto result =
builder.createOrFold<ResultOpType>(args...);
2359 if (
auto *op = result.getDefiningOp())
2361 return setPossiblyFoldedLowering(orig->getResult(0), result);
2368 template <
typename ResultOpType,
typename... CtorArgTypes>
2369 LogicalResult FIRRTLLowering::setLoweringToLTL(Operation *orig,
2370 CtorArgTypes... args) {
2371 auto result =
builder.createOrFold<ResultOpType>(args...);
2372 if (
auto *op = result.getDefiningOp())
2373 ltlOpFixupWorklist.insert(op);
2374 return setPossiblyFoldedLowering(orig->getResult(0), result);
2383 Backedge FIRRTLLowering::createBackedge(Location loc, Type type) {
2384 auto backedge = backedgeBuilder.
get(type, loc);
2385 backedges.insert({backedge, backedge});
2393 Backedge FIRRTLLowering::createBackedge(Value orig, Type type) {
2394 auto backedge = createBackedge(orig.getLoc(), type);
2395 (void)setLowering(orig, backedge);
2401 bool FIRRTLLowering::updateIfBackedge(Value dest, Value src) {
2402 auto backedgeIt = backedges.find(dest);
2403 if (backedgeIt == backedges.end())
2405 backedgeIt->second = src;
2413 void FIRRTLLowering::runWithInsertionPointAtEndOfBlock(
2414 std::function<
void(
void)> fn, Region ®ion) {
2418 auto oldIP =
builder.saveInsertionPoint();
2420 builder.setInsertionPointToEnd(®ion.front());
2422 builder.restoreInsertionPoint(oldIP);
2426 Value FIRRTLLowering::getReadValue(Value v) {
2427 Value result = readInOutCreated.lookup(v);
2433 auto oldIP =
builder.saveInsertionPoint();
2434 if (
auto *vOp = v.getDefiningOp()) {
2435 builder.setInsertionPointAfter(vOp);
2439 builder.setInsertionPoint(&theModule.getBodyBlock()->front());
2444 if (
auto arrayIndexInout = v.getDefiningOp<sv::ArrayIndexInOutOp>()) {
2445 result = getReadValue(arrayIndexInout.getInput());
2446 result =
builder.createOrFold<hw::ArrayGetOp>(result,
2447 arrayIndexInout.getIndex());
2450 result =
builder.createOrFold<sv::ReadInOutOp>(v);
2452 builder.restoreInsertionPoint(oldIP);
2453 readInOutCreated.insert({v, result});
2457 Value FIRRTLLowering::getNonClockValue(Value v) {
2458 auto it = fromClockMapping.try_emplace(v, Value{});
2460 ImplicitLocOpBuilder
builder(v.getLoc(), v.getContext());
2461 builder.setInsertionPointAfterValue(v);
2462 it.first->second =
builder.create<seq::FromClockOp>(v);
2464 return it.first->second;
2467 void FIRRTLLowering::addToAlwaysBlock(sv::EventControl clockEdge, Value clock,
2468 ::ResetType resetStyle,
2469 sv::EventControl resetEdge, Value reset,
2470 std::function<
void(
void)> body,
2471 std::function<
void(
void)> resetBody) {
2472 AlwaysKeyType key{
builder.getBlock(), clockEdge, clock,
2473 resetStyle, resetEdge, reset};
2474 sv::AlwaysOp alwaysOp;
2475 sv::IfOp insideIfOp;
2476 std::tie(alwaysOp, insideIfOp) = alwaysBlocks.lookup(key);
2480 assert(resetStyle != ::ResetType::NoReset);
2493 auto createIfOp = [&]() {
2496 insideIfOp =
builder.create<sv::IfOp>(
2497 reset, []() {}, []() {});
2499 if (resetStyle == ::ResetType::AsyncReset) {
2500 sv::EventControl events[] = {clockEdge, resetEdge};
2501 Value clocks[] = {clock, reset};
2503 alwaysOp =
builder.create<sv::AlwaysOp>(events, clocks, [&]() {
2504 if (resetEdge == sv::EventControl::AtNegEdge)
2505 llvm_unreachable(
"negative edge for reset is not expected");
2509 alwaysOp =
builder.create<sv::AlwaysOp>(clockEdge, clock, createIfOp);
2513 alwaysOp =
builder.create<sv::AlwaysOp>(clockEdge, clock);
2514 insideIfOp =
nullptr;
2516 alwaysBlocks[key] = {alwaysOp, insideIfOp};
2520 assert(insideIfOp &&
"reset body must be initialized before");
2521 runWithInsertionPointAtEndOfBlock(resetBody, insideIfOp.getThenRegion());
2522 runWithInsertionPointAtEndOfBlock(body, insideIfOp.getElseRegion());
2524 runWithInsertionPointAtEndOfBlock(body, alwaysOp.getBody());
2530 alwaysOp->moveBefore(
builder.getInsertionBlock(),
2534 void FIRRTLLowering::addToIfDefBlock(StringRef cond,
2535 std::function<
void(
void)> thenCtor,
2536 std::function<
void(
void)> elseCtor) {
2537 auto condAttr =
builder.getStringAttr(cond);
2538 auto op = ifdefBlocks.lookup({
builder.getBlock(), condAttr});
2540 runWithInsertionPointAtEndOfBlock(thenCtor, op.getThenRegion());
2541 runWithInsertionPointAtEndOfBlock(elseCtor, op.getElseRegion());
2546 op->moveBefore(
builder.getInsertionBlock(),
builder.getInsertionPoint());
2548 ifdefBlocks[{
builder.getBlock(), condAttr}] =
2549 builder.create<sv::IfDefOp>(condAttr, thenCtor, elseCtor);
2553 void FIRRTLLowering::addToInitialBlock(std::function<
void(
void)> body) {
2554 auto op = initialBlocks.lookup(
builder.getBlock());
2556 runWithInsertionPointAtEndOfBlock(body, op.getBody());
2561 op->moveBefore(
builder.getInsertionBlock(),
builder.getInsertionPoint());
2563 initialBlocks[
builder.getBlock()] =
builder.create<sv::InitialOp>(body);
2567 void FIRRTLLowering::addIfProceduralBlock(Value cond,
2568 std::function<
void(
void)> thenCtor,
2569 std::function<
void(
void)> elseCtor) {
2572 auto insertIt =
builder.getInsertionPoint();
2573 if (insertIt !=
builder.getBlock()->begin())
2574 if (
auto ifOp = dyn_cast<sv::IfOp>(*--insertIt)) {
2575 if (ifOp.getCond() == cond) {
2576 runWithInsertionPointAtEndOfBlock(thenCtor, ifOp.getThenRegion());
2577 runWithInsertionPointAtEndOfBlock(elseCtor, ifOp.getElseRegion());
2582 builder.create<sv::IfOp>(cond, thenCtor, elseCtor);
2594 FIRRTLLowering::UnloweredOpResult
2595 FIRRTLLowering::handleUnloweredOp(Operation *op) {
2600 if (!isa<FIRRTLDialect>(op->getDialect())) {
2601 for (
auto &operand : op->getOpOperands())
2602 if (
auto lowered = getPossiblyInoutLoweredValue(operand.get()))
2603 operand.set(lowered);
2604 for (
auto result : op->getResults())
2605 (void)setLowering(result, result);
2606 return AlreadyLowered;
2618 if (op->getNumResults() == 1) {
2619 auto resultType = op->getResult(0).getType();
2620 if (type_isa<FIRRTLBaseType>(resultType) &&
2622 (
isExpression(op) || isa<mlir::UnrealizedConversionCastOp>(op))) {
2624 (void)setLowering(op->getResult(0), Value());
2628 op->emitOpError(
"LowerToHW couldn't handle this operation");
2629 return LoweringFailure;
2632 LogicalResult FIRRTLLowering::visitExpr(ConstantOp op) {
2635 return setLowering(op, Value());
2637 return setLowering(op, getOrCreateIntConstant(op.getValue()));
2640 LogicalResult FIRRTLLowering::visitExpr(SpecialConstantOp op) {
2642 if (op.getType().isa<ClockType>()) {
2643 cst = getOrCreateClockConstant(op.getValue() ? seq::ClockConst::High
2644 : seq::ClockConst::Low);
2646 cst = getOrCreateIntConstant(APInt( 1, op.getValue()));
2648 return setLowering(op, cst);
2651 FailureOr<Value> FIRRTLLowering::lowerSubindex(SubindexOp op, Value input) {
2652 auto iIdx = getOrCreateIntConstant(
2654 firrtl::type_cast<FVectorType>(op.getInput().getType())
2662 result =
builder.createOrFold<sv::ArrayIndexInOutOp>(input, iIdx);
2664 result =
builder.createOrFold<hw::ArrayGetOp>(input, iIdx);
2669 FailureOr<Value> FIRRTLLowering::lowerSubaccess(SubaccessOp op, Value input) {
2670 Value valueIdx = getLoweredAndExtOrTruncValue(
2674 firrtl::type_cast<FVectorType>(op.getInput().getType())
2675 .getNumElements())));
2677 op->emitError() <<
"input lowering failed";
2685 result =
builder.createOrFold<sv::ArrayIndexInOutOp>(input, valueIdx);
2687 result = createArrayIndexing(input, valueIdx);
2692 FailureOr<Value> FIRRTLLowering::lowerSubfield(SubfieldOp op, Value input) {
2693 auto resultType =
lowerType(op->getResult(0).getType());
2694 if (!resultType || !input) {
2695 op->emitError() <<
"subfield type lowering failed";
2701 auto field = firrtl::type_cast<BundleType>(op.getInput().getType())
2702 .getElementName(op.getFieldIndex());
2705 result =
builder.createOrFold<sv::StructFieldInOutOp>(input, field);
2707 result =
builder.createOrFold<hw::StructExtractOp>(input, field);
2712 LogicalResult FIRRTLLowering::visitExpr(SubindexOp op) {
2714 return setLowering(op, Value());
2716 auto input = getPossiblyInoutLoweredValue(op.getInput());
2718 return op.emitError() <<
"input lowering failed";
2720 auto result = lowerSubindex(op, input);
2723 return setLowering(op, *result);
2726 LogicalResult FIRRTLLowering::visitExpr(SubaccessOp op) {
2728 return setLowering(op, Value());
2730 auto input = getPossiblyInoutLoweredValue(op.getInput());
2732 return op.emitError() <<
"input lowering failed";
2734 auto result = lowerSubaccess(op, input);
2737 return setLowering(op, *result);
2740 LogicalResult FIRRTLLowering::visitExpr(SubfieldOp op) {
2743 if (getLoweredValue(op) || !op.getInput())
2747 return setLowering(op, Value());
2749 auto input = getPossiblyInoutLoweredValue(op.getInput());
2751 return op.emitError() <<
"input lowering failed";
2753 auto result = lowerSubfield(op, input);
2756 return setLowering(op, *result);
2759 LogicalResult FIRRTLLowering::visitExpr(VectorCreateOp op) {
2760 auto resultType =
lowerType(op.getResult().getType());
2761 SmallVector<Value> operands;
2763 for (
auto oper : llvm::reverse(op.getOperands())) {
2764 auto val = getLoweredValue(oper);
2767 operands.push_back(val);
2769 return setLoweringTo<hw::ArrayCreateOp>(op, resultType, operands);
2772 LogicalResult FIRRTLLowering::visitExpr(BundleCreateOp op) {
2773 auto resultType =
lowerType(op.getResult().getType());
2774 SmallVector<Value> operands;
2775 for (
auto oper : op.getOperands()) {
2776 auto val = getLoweredValue(oper);
2779 operands.push_back(val);
2781 return setLoweringTo<hw::StructCreateOp>(op, resultType, operands);
2784 LogicalResult FIRRTLLowering::visitExpr(FEnumCreateOp op) {
2787 return setLowering(op, Value());
2789 auto input = getLoweredValue(op.getInput());
2790 auto tagName = op.getFieldNameAttr();
2793 if (
auto structType = dyn_cast<hw::StructType>(type)) {
2794 auto enumType = structType.getFieldType(
"tag");
2796 auto enumOp =
builder.create<hw::EnumConstantOp>(enumAttr);
2797 auto unionType = structType.getFieldType(
"body");
2798 auto unionOp =
builder.create<hw::UnionCreateOp>(unionType, tagName, input);
2799 SmallVector<Value> operands = {enumOp.getResult(), unionOp.getResult()};
2800 return setLoweringTo<hw::StructCreateOp>(op, structType, operands);
2803 return setLoweringTo<hw::EnumConstantOp>(
2807 LogicalResult FIRRTLLowering::visitExpr(AggregateConstantOp op) {
2808 auto resultType =
lowerType(op.getResult().getType());
2810 getOrCreateAggregateConstantAttribute(op.getFieldsAttr(), resultType);
2812 return setLoweringTo<hw::AggregateConstantOp>(op, resultType,
2813 cast<ArrayAttr>(attr));
2816 LogicalResult FIRRTLLowering::visitExpr(IsTagOp op) {
2817 auto tagName = op.getFieldNameAttr();
2818 auto lhs = getLoweredValue(op.getInput());
2819 if (isa<hw::StructType>(lhs.getType()))
2820 lhs =
builder.create<hw::StructExtractOp>(lhs,
"tag");
2822 auto rhs =
builder.create<hw::EnumConstantOp>(enumField);
2823 return setLoweringTo<hw::EnumCmpOp>(op, lhs, rhs);
2826 LogicalResult FIRRTLLowering::visitExpr(SubtagOp op) {
2829 return setLowering(op, Value());
2831 auto tagName = op.getFieldNameAttr();
2832 auto input = getLoweredValue(op.getInput());
2833 auto field =
builder.create<hw::StructExtractOp>(input,
"body");
2834 return setLoweringTo<hw::UnionExtractOp>(op, field, tagName);
2841 LogicalResult FIRRTLLowering::visitDecl(WireOp op) {
2842 auto origResultType = op.getResult().getType();
2846 if (!type_isa<FIRRTLType>(origResultType)) {
2847 createBackedge(op.getResult(), origResultType);
2851 auto resultType =
lowerType(origResultType);
2855 if (resultType.isInteger(0))
2856 return setLowering(op.getResult(), Value());
2859 auto innerSym = lowerInnerSymbol(op);
2860 auto name = op.getNameAttr();
2863 auto wire =
builder.create<hw::WireOp>(
2864 op.getLoc(), getOrCreateZConstant(resultType), name, innerSym);
2869 return setLowering(op.getResult(), wire);
2872 LogicalResult FIRRTLLowering::visitDecl(VerbatimWireOp op) {
2873 auto resultTy =
lowerType(op.getType());
2878 SmallVector<Value, 4> operands;
2879 operands.reserve(op.getSubstitutions().size());
2880 for (
auto operand : op.getSubstitutions()) {
2881 auto lowered = getLoweredValue(operand);
2884 operands.push_back(lowered);
2887 ArrayAttr symbols = op.getSymbolsAttr();
2891 return setLoweringTo<sv::VerbatimExprSEOp>(op, resultTy, op.getTextAttr(),
2895 LogicalResult FIRRTLLowering::visitDecl(NodeOp op) {
2896 auto operand = getLoweredValue(op.getInput());
2899 op.getInput(), [&]() { return setLowering(op.getResult(), Value()); });
2904 auto name = op.getNameAttr();
2905 auto innerSym = lowerInnerSymbol(op);
2908 operand =
builder.create<hw::WireOp>(operand, name, innerSym);
2913 operand =
builder.create<hw::WireOp>(operand, name);
2917 return setLowering(op.getResult(), operand);
2920 LogicalResult FIRRTLLowering::visitDecl(RegOp op) {
2921 auto resultType =
lowerType(op.getResult().getType());
2924 if (resultType.isInteger(0))
2925 return setLowering(op.getResult(), Value());
2927 Value clockVal = getLoweredValue(op.getClockVal());
2932 auto innerSym = lowerInnerSymbol(op);
2933 Backedge inputEdge = backedgeBuilder.
get(resultType);
2934 auto reg =
builder.create<seq::FirRegOp>(inputEdge, clockVal,
2935 op.getNameAttr(), innerSym);
2938 if (
auto randomRegister = op->getAttr(
"firrtl.random_init_register"))
2939 reg->setAttr(
"firrtl.random_init_register", randomRegister);
2940 if (
auto randomStart = op->getAttr(
"firrtl.random_init_start"))
2941 reg->setAttr(
"firrtl.random_init_start", randomStart);
2942 if (
auto randomEnd = op->getAttr(
"firrtl.random_init_end"))
2943 reg->setAttr(
"firrtl.random_init_end", randomEnd);
2949 inputEdge.setValue(
reg);
2950 circuitState.used_RANDOMIZE_REG_INIT =
true;
2951 (void)setLowering(op.getResult(),
reg);
2955 LogicalResult FIRRTLLowering::visitDecl(RegResetOp op) {
2956 auto resultType =
lowerType(op.getResult().getType());
2959 if (resultType.isInteger(0))
2960 return setLowering(op.getResult(), Value());
2962 Value clockVal = getLoweredValue(op.getClockVal());
2963 Value resetSignal = getLoweredValue(op.getResetSignal());
2965 Value resetValue = getLoweredAndExtOrTruncValue(
2966 op.getResetValue(), type_cast<FIRRTLBaseType>(op.getResult().getType()));
2968 if (!clockVal || !resetSignal || !resetValue)
2972 auto innerSym = lowerInnerSymbol(op);
2973 bool isAsync = type_isa<AsyncResetType>(op.getResetSignal().getType());
2974 Backedge inputEdge = backedgeBuilder.
get(resultType);
2976 builder.create<seq::FirRegOp>(inputEdge, clockVal, op.getNameAttr(),
2977 resetSignal, resetValue, innerSym, isAsync);
2980 if (
auto randomRegister = op->getAttr(
"firrtl.random_init_register"))
2981 reg->setAttr(
"firrtl.random_init_register", randomRegister);
2982 if (
auto randomStart = op->getAttr(
"firrtl.random_init_start"))
2983 reg->setAttr(
"firrtl.random_init_start", randomStart);
2984 if (
auto randomEnd = op->getAttr(
"firrtl.random_init_end"))
2985 reg->setAttr(
"firrtl.random_init_end", randomEnd);
2991 inputEdge.setValue(
reg);
2992 circuitState.used_RANDOMIZE_REG_INIT =
true;
2993 (void)setLowering(op.getResult(),
reg);
2998 LogicalResult FIRRTLLowering::visitDecl(MemOp op) {
3001 if (type_isa<BundleType>(op.getDataType()))
3002 return op.emitOpError(
3003 "should have already been lowered from a ground type to an aggregate "
3004 "type using the LowerTypes pass. Use "
3005 "'firtool --lower-types' or 'circt-opt "
3006 "--pass-pipeline='firrtl.circuit(firrtl-lower-types)' "
3015 : std::optional<uint32_t>());
3017 seq::FirMemInitAttr memInit;
3018 if (
auto init = op.getInitAttr())
3020 init.getIsBinary(), init.getIsInline());
3022 auto memDecl =
builder.create<seq::FirMemOp>(
3025 op.getInnerSymAttr(), memInit, op.getPrefixAttr(), Attribute{});
3029 if (!circuitState.isInDUT(theModule))
3030 if (
auto testBenchDir = circuitState.getTestBenchDirectory())
3031 memDecl.setOutputFileAttr(testBenchDir);
3035 for (
size_t i = 0, e = op.getNumResults(); i != e; ++i) {
3037 auto addOutput = [&](StringRef field,
size_t width, Value value) {
3040 (void)setLowering(a, value);
3046 auto addInput = [&](StringRef field, Value backedge) {
3051 .getBitWidthOrSentinel() > 0)
3052 (
void)setLowering(a, backedge);
3058 auto addInputPort = [&](StringRef field,
size_t width) -> Value {
3062 Value backedge, portValue;
3064 portValue = getOrCreateXConstant(1);
3067 backedge = portValue = createBackedge(
builder.getLoc(), portType);
3069 addInput(field, backedge);
3073 auto addClock = [&](StringRef field) -> Value {
3075 Value portValue = createBackedge(
builder.getLoc(), clockTy);
3076 addInput(field, portValue);
3080 auto memportKind = op.getPortKind(i);
3081 if (memportKind == MemOp::PortKind::Read) {
3082 auto addr = addInputPort(
"addr", op.getAddrBits());
3083 auto en = addInputPort(
"en", 1);
3084 auto clk = addClock(
"clk");
3086 addOutput(
"data", memSummary.
dataWidth, data);
3087 }
else if (memportKind == MemOp::PortKind::ReadWrite) {
3088 auto addr = addInputPort(
"addr", op.getAddrBits());
3089 auto en = addInputPort(
"en", 1);
3090 auto clk = addClock(
"clk");
3093 auto mode = addInputPort(
"wmode", 1);
3095 mode =
builder.createOrFold<comb::AndOp>(mode, addInputPort(
"wmask", 1),
3104 addOutput(
"rdata", memSummary.
dataWidth, rdata);
3106 auto addr = addInputPort(
"addr", op.getAddrBits());
3109 auto en = addInputPort(
"en", 1);
3111 en =
builder.createOrFold<comb::AndOp>(
en, addInputPort(
"mask", 1),
3113 auto clk = addClock(
"clk");
3123 circuitState.used_RANDOMIZE_MEM_INIT =
true;
3127 LogicalResult FIRRTLLowering::visitDecl(InstanceOp oldInstance) {
3128 Operation *oldModule =
3129 circuitState.getInstanceGraph()->getReferencedModule(oldInstance);
3130 auto newModule = circuitState.getNewModule(oldModule);
3132 oldInstance->emitOpError(
"could not find module [")
3133 << oldInstance.getModuleName() <<
"] referenced by instance";
3139 ArrayAttr parameters;
3140 if (
auto oldExtModule = dyn_cast<FExtModuleOp>(oldModule))
3145 SmallVector<PortInfo, 8> portInfo = cast<FModuleLike>(oldModule).getPorts();
3150 for (
unsigned portIdx = 0, e = portInfo.size(); portIdx != e; ++portIdx)
3151 portIndicesByName[portInfo[portIdx].name] = portIdx;
3155 SmallVector<Value, 8> operands;
3156 for (
size_t portIndex = 0, e = portInfo.size(); portIndex != e; ++portIndex) {
3157 auto &port = portInfo[portIndex];
3160 oldInstance->emitOpError(
"could not lower type of port ") << port.name;
3165 if (portType.isInteger(0))
3169 if (port.isOutput())
3172 auto portResult = oldInstance.getResult(portIndex);
3173 assert(portResult &&
"invalid IR, couldn't find port");
3177 if (port.isInput()) {
3178 operands.push_back(createBackedge(portResult, portType));
3184 if (type_isa<AnalogType>(portResult.getType()) && portResult.hasOneUse()) {
3185 if (
auto attach = dyn_cast<AttachOp>(*portResult.getUsers().begin())) {
3187 auto loweredResult = getPossiblyInoutLoweredValue(source);
3188 operands.push_back(loweredResult);
3189 (void)setLowering(portResult, loweredResult);
3197 auto wire =
builder.create<sv::WireOp>(
3198 portType,
"." + port.getName().str() +
".wire");
3202 (void)setLowering(portResult, wire);
3204 operands.push_back(wire);
3211 auto innerSym = oldInstance.getInnerSymAttr();
3212 if (oldInstance.getLowerToBind()) {
3215 builder.getStringAttr(
"__" + oldInstance.getName() +
"__"));
3216 auto bindOp =
builder.create<sv::BindOp>(theModule.getNameAttr(),
3217 innerSym.getSymName());
3220 if (
auto outputFile = oldInstance->getAttr(
"output_file"))
3221 bindOp->setAttr(
"output_file", outputFile);
3224 circuitState.addBind(bindOp);
3228 auto newInstance =
builder.create<hw::InstanceOp>(
3229 newModule, oldInstance.getNameAttr(), operands, parameters, innerSym);
3231 if (oldInstance.getLowerToBind())
3232 newInstance->setAttr(
"doNotPrint",
builder.getBoolAttr(
true));
3234 if (newInstance.getInnerSymAttr())
3235 if (
auto forceName = circuitState.instanceForceNames.lookup(
3236 {cast<hw::HWModuleOp>(newInstance->getParentOp()).getNameAttr(),
3237 newInstance.getInnerNameAttr()}))
3238 newInstance->setAttr(
"hw.verilogName", forceName);
3242 unsigned resultNo = 0;
3243 for (
size_t portIndex = 0, e = portInfo.size(); portIndex != e; ++portIndex) {
3244 auto &port = portInfo[portIndex];
3248 Value resultVal = newInstance.getResult(resultNo);
3250 auto oldPortResult = oldInstance.getResult(portIndex);
3251 (void)setLowering(oldPortResult, resultVal);
3262 LogicalResult FIRRTLLowering::lowerNoopCast(Operation *op) {
3263 auto operand = getPossiblyInoutLoweredValue(op->getOperand(0));
3268 return setLowering(op->getResult(0), operand);
3271 LogicalResult FIRRTLLowering::visitExpr(AsSIntPrimOp op) {
3272 if (isa<ClockType>(op.getInput().getType()))
3273 return setLowering(op->getResult(0),
3274 getLoweredNonClockValue(op.getInput()));
3275 return lowerNoopCast(op);
3278 LogicalResult FIRRTLLowering::visitExpr(AsUIntPrimOp op) {
3279 if (isa<ClockType>(op.getInput().getType()))
3280 return setLowering(op->getResult(0),
3281 getLoweredNonClockValue(op.getInput()));
3282 return lowerNoopCast(op);
3285 LogicalResult FIRRTLLowering::visitExpr(AsClockPrimOp op) {
3286 return setLoweringTo<seq::ToClockOp>(op, getLoweredValue(op.getInput()));
3289 LogicalResult FIRRTLLowering::visitExpr(mlir::UnrealizedConversionCastOp op) {
3291 if (op.getNumOperands() != 1 || op.getNumResults() != 1)
3294 auto operand = op.getOperand(0);
3295 auto result = op.getResult(0);
3298 if (type_isa<FIRRTLType>(operand.getType()) &&
3299 type_isa<FIRRTLType>(result.getType()))
3300 return lowerNoopCast(op);
3304 if (!type_isa<FIRRTLType>(operand.getType())) {
3305 if (type_isa<FIRRTLType>(result.getType()))
3306 return setLowering(result, getPossiblyInoutLoweredValue(operand));
3312 auto lowered_result = getLoweredValue(operand);
3313 if (!lowered_result) {
3316 if (operand.getType().isSignlessInteger(0)) {
3317 return setLowering(result, Value());
3324 result.replaceAllUsesWith(lowered_result);
3328 LogicalResult FIRRTLLowering::visitExpr(HWStructCastOp op) {
3331 if (
auto opStructType = op.getOperand().getType().dyn_cast<hw::StructType>())
3332 return setLowering(op, op.getOperand());
3336 auto result = getLoweredValue(op.getOperand());
3342 op.replaceAllUsesWith(result);
3346 LogicalResult FIRRTLLowering::visitExpr(BitCastOp op) {
3347 auto operand = getLoweredValue(op.getOperand());
3350 auto resultType =
lowerType(op.getType());
3354 return setLoweringTo<hw::BitcastOp>(op, resultType, operand);
3357 LogicalResult FIRRTLLowering::visitExpr(CvtPrimOp op) {
3358 auto operand = getLoweredValue(op.getOperand());
3362 if (type_cast<IntType>(op.getOperand().getType()).isUnsigned())
3363 return setLowering(op, getOrCreateIntConstant(1, 0));
3365 return setLowering(op, Value());
3370 if (type_cast<IntType>(op.getOperand().getType()).isSigned())
3371 return setLowering(op, operand);
3374 auto zero = getOrCreateIntConstant(1, 0);
3375 return setLoweringTo<comb::ConcatOp>(op, zero, operand);
3378 LogicalResult FIRRTLLowering::visitExpr(NotPrimOp op) {
3379 auto operand = getLoweredValue(op.getInput());
3383 auto allOnes = getOrCreateIntConstant(
3384 APInt::getAllOnes(operand.getType().getIntOrFloatBitWidth()));
3385 return setLoweringTo<comb::XorOp>(op, operand, allOnes,
true);
3388 LogicalResult FIRRTLLowering::visitExpr(NegPrimOp op) {
3391 auto operand = getLoweredAndExtendedValue(op.getInput(), op.getType());
3395 auto resultType =
lowerType(op.getType());
3397 auto zero = getOrCreateIntConstant(resultType.getIntOrFloatBitWidth(), 0);
3398 return setLoweringTo<comb::SubOp>(op, zero, operand,
true);
3402 LogicalResult FIRRTLLowering::visitExpr(PadPrimOp op) {
3403 auto operand = getLoweredAndExtendedValue(op.getInput(), op.getType());
3406 return setLowering(op, operand);
3409 LogicalResult FIRRTLLowering::visitExpr(XorRPrimOp op) {
3410 auto operand = getLoweredValue(op.getInput());
3413 return setLowering(op, getOrCreateIntConstant(1, 0));
3418 return setLoweringTo<comb::ParityOp>(op,
builder.getIntegerType(1), operand,
3422 LogicalResult FIRRTLLowering::visitExpr(AndRPrimOp op) {
3423 auto operand = getLoweredValue(op.getInput());
3426 return setLowering(op, getOrCreateIntConstant(1, 1));
3431 return setLoweringTo<comb::ICmpOp>(
3432 op, ICmpPredicate::eq, operand,
3433 getOrCreateIntConstant(
3434 APInt::getAllOnes(operand.getType().getIntOrFloatBitWidth())),
3438 LogicalResult FIRRTLLowering::visitExpr(OrRPrimOp op) {
3439 auto operand = getLoweredValue(op.getInput());
3442 return setLowering(op, getOrCreateIntConstant(1, 0));
3448 return setLoweringTo<comb::ICmpOp>(
3449 op, ICmpPredicate::ne, operand,
3450 getOrCreateIntConstant(operand.getType().getIntOrFloatBitWidth(), 0),
3458 template <
typename ResultOpType>
3459 LogicalResult FIRRTLLowering::lowerBinOpToVariadic(Operation *op) {
3460 auto resultType = op->getResult(0).getType();
3461 auto lhs = getLoweredAndExtendedValue(op->getOperand(0), resultType);
3462 auto rhs = getLoweredAndExtendedValue(op->getOperand(1), resultType);
3466 return setLoweringTo<ResultOpType>(op, lhs, rhs,
true);
3472 template <
typename ResultOpType>
3473 LogicalResult FIRRTLLowering::lowerElementwiseLogicalOp(Operation *op) {
3474 auto resultType = op->getResult(0).getType();
3475 auto lhs = getLoweredAndExtendedValue(op->getOperand(0), resultType);
3476 auto rhs = getLoweredAndExtendedValue(op->getOperand(1), resultType);
3487 auto intType =
builder.getIntegerType(*bitwidth);
3488 auto retType = lhs.getType();
3489 lhs =
builder.createOrFold<hw::BitcastOp>(intType, lhs);
3490 rhs =
builder.createOrFold<hw::BitcastOp>(intType, rhs);
3491 auto result =
builder.createOrFold<ResultOpType>(lhs, rhs,
true);
3492 return setLoweringTo<hw::BitcastOp>(op, retType, result);
3497 template <
typename ResultUn
signedOpType,
typename ResultSignedOpType>
3498 LogicalResult FIRRTLLowering::lowerBinOp(Operation *op) {
3500 auto resultType = op->getResult(0).getType();
3501 auto lhs = getLoweredAndExtendedValue(op->getOperand(0), resultType);
3502 auto rhs = getLoweredAndExtendedValue(op->getOperand(1), resultType);
3507 if (type_cast<IntType>(resultType).isSigned())
3508 return setLoweringTo<ResultSignedOpType>(op, lhs, rhs,
true);
3509 return setLoweringTo<ResultUnsignedOpType>(op, lhs, rhs,
true);
3514 LogicalResult FIRRTLLowering::lowerCmpOp(Operation *op, ICmpPredicate signedOp,
3515 ICmpPredicate unsignedOp) {
3517 auto lhsIntType = type_cast<IntType>(op->getOperand(0).getType());
3518 auto rhsIntType = type_cast<IntType>(op->getOperand(1).getType());
3519 if (!lhsIntType.hasWidth() || !rhsIntType.hasWidth())
3523 if (cmpType.getWidth() == 0)
3525 auto lhs = getLoweredAndExtendedValue(op->getOperand(0), cmpType);
3526 auto rhs = getLoweredAndExtendedValue(op->getOperand(1), cmpType);
3531 Type resultType =
builder.getIntegerType(1);
3532 return setLoweringTo<comb::ICmpOp>(
3533 op, resultType, lhsIntType.isSigned() ? signedOp : unsignedOp, lhs, rhs,
3539 template <
typename SignedOp,
typename Un
signedOp>
3540 LogicalResult FIRRTLLowering::lowerDivLikeOp(Operation *op) {
3544 auto opType = type_cast<IntType>(op->getResult(0).getType());
3545 if (opType.getWidth() == 0)
3546 return setLowering(op->getResult(0), Value());
3550 auto lhs = getLoweredAndExtendedValue(op->getOperand(0), resultType);
3551 auto rhs = getLoweredAndExtendedValue(op->getOperand(1), resultType);
3556 if (opType.isSigned())
3557 result =
builder.createOrFold<SignedOp>(lhs, rhs,
true);
3559 result =
builder.createOrFold<UnsignedOp>(lhs, rhs,
true);
3563 if (resultType == opType)
3564 return setLowering(op->getResult(0), result);
3565 return setLoweringTo<comb::ExtractOp>(op,
lowerType(opType), result, 0);
3568 LogicalResult FIRRTLLowering::visitExpr(CatPrimOp op) {
3569 auto lhs = getLoweredValue(op.getLhs());
3570 auto rhs = getLoweredValue(op.getRhs());
3574 return setLowering(op, rhs);
3576 return handleZeroBit(op.getRhs(),
3577 [&]() { return setLowering(op, Value()); });
3582 return handleZeroBit(op.getRhs(), [&]() { return setLowering(op, lhs); });
3584 return setLoweringTo<comb::ConcatOp>(op, lhs, rhs);
3591 LogicalResult FIRRTLLowering::visitExpr(IsXIntrinsicOp op) {
3592 auto input = getLoweredNonClockValue(op.getArg());
3596 return setLoweringTo<comb::ICmpOp>(
3597 op, ICmpPredicate::ceq, input,
3598 getOrCreateXConstant(input.getType().getIntOrFloatBitWidth()),
true);
3601 LogicalResult FIRRTLLowering::visitExpr(PlusArgsTestIntrinsicOp op) {
3602 auto resultType =
builder.getIntegerType(1);
3603 auto str =
builder.create<sv::ConstantStrOp>(op.getFormatString());
3605 builder.create<sv::RegOp>(resultType,
builder.getStringAttr(
"_pargs"));
3606 addToInitialBlock([&]() {
3607 auto call =
builder.create<sv::SystemFunctionOp>(
3608 resultType,
"test$plusargs", ArrayRef<Value>{str});
3611 return setLoweringTo<sv::ReadInOutOp>(op,
reg);
3614 LogicalResult FIRRTLLowering::visitExpr(PlusArgsValueIntrinsicOp op) {
3615 auto resultType =
builder.getIntegerType(1);
3616 auto type =
lowerType(op.getResult().getType());
3620 builder.create<sv::RegOp>(type,
builder.getStringAttr(
"_pargs_v_"));
3622 builder.create<sv::RegOp>(resultType,
builder.getStringAttr(
"_pargs_f"));
3626 auto cst0 = getOrCreateIntConstant(1, 0);
3627 builder.create<sv::AssignOp>(regf, cst0);
3630 addToInitialBlock([&]() {
3631 auto zero32 = getOrCreateIntConstant(32, 0);
3632 auto tmpResultType =
builder.getIntegerType(32);
3633 auto str =
builder.create<sv::ConstantStrOp>(op.getFormatString());
3634 auto call =
builder.create<sv::SystemFunctionOp>(
3635 tmpResultType,
"value$plusargs", ArrayRef<Value>{str, regv});
3636 auto truevalue =
builder.create<comb::ICmpOp>(ICmpPredicate::ne, call,
3638 builder.create<sv::BPAssignOp>(regf, truevalue);
3641 auto readf =
builder.create<sv::ReadInOutOp>(regf);
3642 auto readv =
builder.create<sv::ReadInOutOp>(regv);
3644 (void)setLowering(op.getResult(), readv);
3645 return setLowering(op.getFound(), readf);
3648 LogicalResult FIRRTLLowering::visitExpr(SizeOfIntrinsicOp op) {
3649 op.emitError(
"SizeOf should have been resolved.");
3653 LogicalResult FIRRTLLowering::visitExpr(ClockGateIntrinsicOp op) {
3655 if (op.getTestEnable())
3656 testEnable = getLoweredValue(op.getTestEnable());
3657 return setLoweringTo<seq::ClockGateOp>(
3658 op, getLoweredValue(op.getInput()), getLoweredValue(op.getEnable()),
3659 testEnable, hw::InnerSymAttr{});
3662 LogicalResult FIRRTLLowering::visitExpr(LTLAndIntrinsicOp op) {
3663 return setLoweringToLTL<ltl::AndOp>(
3665 ValueRange{getLoweredValue(op.getLhs()), getLoweredValue(op.getRhs())});
3668 LogicalResult FIRRTLLowering::visitExpr(LTLOrIntrinsicOp op) {
3669 return setLoweringToLTL<ltl::OrOp>(
3671 ValueRange{getLoweredValue(op.getLhs()), getLoweredValue(op.getRhs())});
3674 LogicalResult FIRRTLLowering::visitExpr(LTLDelayIntrinsicOp op) {
3675 return setLoweringToLTL<ltl::DelayOp>(op, getLoweredValue(op.getInput()),
3676 op.getDelayAttr(), op.getLengthAttr());
3679 LogicalResult FIRRTLLowering::visitExpr(LTLConcatIntrinsicOp op) {
3680 return setLoweringToLTL<ltl::ConcatOp>(
3682 ValueRange{getLoweredValue(op.getLhs()), getLoweredValue(op.getRhs())});
3685 LogicalResult FIRRTLLowering::visitExpr(LTLNotIntrinsicOp op) {
3686 return setLoweringToLTL<ltl::NotOp>(op, getLoweredValue(op.getInput()));
3689 LogicalResult FIRRTLLowering::visitExpr(LTLImplicationIntrinsicOp op) {
3690 return setLoweringToLTL<ltl::ImplicationOp>(
3692 ValueRange{getLoweredValue(op.getLhs()), getLoweredValue(op.getRhs())});
3695 LogicalResult FIRRTLLowering::visitExpr(LTLEventuallyIntrinsicOp op) {
3696 return setLoweringToLTL<ltl::EventuallyOp>(op,
3697 getLoweredValue(op.getInput()));
3700 LogicalResult FIRRTLLowering::visitExpr(LTLClockIntrinsicOp op) {
3701 return setLoweringToLTL<ltl::ClockOp>(op, getLoweredValue(op.getInput()),
3702 ltl::ClockEdge::Pos,
3703 getLoweredNonClockValue(op.getClock()));
3706 LogicalResult FIRRTLLowering::visitExpr(LTLDisableIntrinsicOp op) {
3707 return setLoweringToLTL<ltl::DisableOp>(
3709 ValueRange{getLoweredValue(op.getLhs()), getLoweredValue(op.getRhs())});
3712 LogicalResult FIRRTLLowering::visitStmt(VerifAssertIntrinsicOp op) {
3713 builder.create<verif::AssertOp>(getLoweredValue(op.getProperty()),
3718 LogicalResult FIRRTLLowering::visitStmt(VerifAssumeIntrinsicOp op) {
3719 builder.create<verif::AssumeOp>(getLoweredValue(op.getProperty()),
3724 LogicalResult FIRRTLLowering::visitStmt(VerifCoverIntrinsicOp op) {
3725 builder.create<verif::CoverOp>(getLoweredValue(op.getProperty()),
3730 LogicalResult FIRRTLLowering::visitExpr(HasBeenResetIntrinsicOp op) {
3731 auto clock = getLoweredNonClockValue(op.getClock());
3732 auto reset = getLoweredValue(op.getReset());
3733 if (!clock || !reset)
3735 auto resetType = op.getReset().getType();
3736 auto uintResetType = dyn_cast<UIntType>(resetType);
3737 auto isSync = uintResetType && uintResetType.getWidth() == 1;
3738 auto isAsync = isa<AsyncResetType>(resetType);
3739 if (!isAsync && !isSync) {
3740 auto d = op.emitError(
"uninferred reset passed to 'has_been_reset'; "
3741 "requires sync or async reset");
3742 d.attachNote() <<
"reset is of type " << resetType
3743 <<
", should be '!firrtl.uint<1>' or '!firrtl.asyncreset'";
3746 return setLoweringTo<verif::HasBeenResetOp>(op, clock, reset, isAsync);
3753 LogicalResult FIRRTLLowering::visitExpr(BitsPrimOp op) {
3754 auto input = getLoweredValue(op.getInput());
3758 Type resultType =
builder.getIntegerType(op.getHi() - op.getLo() + 1);
3759 return setLoweringTo<comb::ExtractOp>(op, resultType, input, op.getLo());
3762 LogicalResult FIRRTLLowering::visitExpr(InvalidValueOp op) {
3763 auto resultTy =
lowerType(op.getType());
3770 if (type_isa<AnalogType>(op.getType()))
3773 return setLoweringTo<sv::WireOp>(op, resultTy,
".invalid_analog");
3776 if (type_cast<FIRRTLBaseType>(op.getType()).containsAnalog())
3787 auto constant = getOrCreateIntConstant(*bitwidth, 0);
3789 if (!type_isa<IntegerType>(resultTy))
3790 constant =
builder.create<hw::BitcastOp>(resultTy, constant);
3791 return setLowering(op, constant);
3795 op.emitOpError(
"unsupported type");
3799 LogicalResult FIRRTLLowering::visitExpr(HeadPrimOp op) {
3800 auto input = getLoweredValue(op.getInput());
3803 auto inWidth = type_cast<IntegerType>(input.getType()).getWidth();
3804 if (op.getAmount() == 0)
3805 return setLowering(op, Value());
3806 Type resultType =
builder.getIntegerType(op.getAmount());
3807 return setLoweringTo<comb::ExtractOp>(op, resultType, input,
3808 inWidth - op.getAmount());
3811 LogicalResult FIRRTLLowering::visitExpr(ShlPrimOp op) {
3812 auto input = getLoweredValue(op.getInput());
3815 if (op.getAmount() == 0)
3817 return setLowering(op, getOrCreateIntConstant(op.getAmount(), 0));
3822 if (op.getAmount() == 0)
3823 return setLowering(op, input);
3825 auto zero = getOrCreateIntConstant(op.getAmount(), 0);
3826 return setLoweringTo<comb::ConcatOp>(op, input, zero);
3829 LogicalResult FIRRTLLowering::visitExpr(ShrPrimOp op) {
3830 auto input = getLoweredValue(op.getInput());
3835 auto inWidth = type_cast<IntegerType>(input.getType()).getWidth();
3836 auto shiftAmount = op.getAmount();
3837 if (shiftAmount >= inWidth) {
3839 if (type_cast<IntType>(op.getInput().getType()).isUnsigned())
3840 return setLowering(op, getOrCreateIntConstant(1, 0));
3843 shiftAmount = inWidth - 1;
3846 Type resultType =
builder.getIntegerType(inWidth - shiftAmount);
3847 return setLoweringTo<comb::ExtractOp>(op, resultType, input, shiftAmount);
3850 LogicalResult FIRRTLLowering::visitExpr(TailPrimOp op) {
3851 auto input = getLoweredValue(op.getInput());
3855 auto inWidth = type_cast<IntegerType>(input.getType()).getWidth();
3856 if (inWidth == op.getAmount())
3857 return setLowering(op, Value());
3858 Type resultType =
builder.getIntegerType(inWidth - op.getAmount());
3859 return setLoweringTo<comb::ExtractOp>(op, resultType, input, 0);
3862 LogicalResult FIRRTLLowering::visitExpr(MuxPrimOp op) {
3863 auto cond = getLoweredValue(op.getSel());
3864 auto ifTrue = getLoweredAndExtendedValue(op.getHigh(), op.getType());
3865 auto ifFalse = getLoweredAndExtendedValue(op.getLow(), op.getType());
3866 if (!cond || !ifTrue || !ifFalse)
3869 if (op.getType().isa<ClockType>())
3870 return setLoweringTo<seq::ClockMuxOp>(op, cond, ifTrue, ifFalse);
3871 return setLoweringTo<comb::MuxOp>(op, ifTrue.getType(), cond, ifTrue, ifFalse,
3875 LogicalResult FIRRTLLowering::visitExpr(Mux2CellIntrinsicOp op) {
3876 auto cond = getLoweredValue(op.getSel());
3877 auto ifTrue = getLoweredAndExtendedValue(op.getHigh(), op.getType());
3878 auto ifFalse = getLoweredAndExtendedValue(op.getLow(), op.getType());
3879 if (!cond || !ifTrue || !ifFalse)
3882 auto val =
builder.create<comb::MuxOp>(ifTrue.getType(), cond, ifTrue,
3884 return setLowering(op, createValueWithMuxAnnotation(val,
true));
3887 LogicalResult FIRRTLLowering::visitExpr(Mux4CellIntrinsicOp op) {
3888 auto sel = getLoweredValue(op.getSel());
3889 auto v3 = getLoweredAndExtendedValue(op.getV3(), op.getType());
3890 auto v2 = getLoweredAndExtendedValue(op.getV2(), op.getType());
3891 auto v1 = getLoweredAndExtendedValue(op.getV1(), op.getType());
3892 auto v0 = getLoweredAndExtendedValue(op.getV0(), op.getType());
3893 if (!sel || !v3 || !v2 || !v1 || !v0)
3895 Value array[] = {v3, v2, v1, v0};
3896 auto create =
builder.create<hw::ArrayCreateOp>(array);
3897 auto val =
builder.create<hw::ArrayGetOp>(create, sel);
3898 return setLowering(op, createValueWithMuxAnnotation(val,
false));
3917 Value FIRRTLLowering::createValueWithMuxAnnotation(Operation *op,
bool isMux2) {
3918 assert(op->getNumResults() == 1 &&
"only expect a single result");
3919 auto val = op->getResult(0);
3920 auto valWire =
builder.create<sv::WireOp>(val.getType());
3930 OpBuilder::InsertionGuard guard(
builder);
3931 builder.setInsertionPoint(op);
3932 StringRef namehint = isMux2 ?
"mux2cell_in" :
"mux4cell_in";
3933 for (
auto [idx, operand] : llvm::enumerate(op->getOperands())) {
3935 op->getContext(),
nullptr, 0,
3936 [&]() -> hw::InnerSymbolNamespace & { return moduleNamespace; });
3938 builder.create<hw::WireOp>(operand, namehint + Twine(idx), innerSym);
3939 op->setOperand(idx, wire);
3943 auto assignOp =
builder.create<sv::AssignOp>(valWire, val);
3946 "synopsys infer_mux_override",
3948 return builder.create<sv::ReadInOutOp>(valWire);
3951 Value FIRRTLLowering::createArrayIndexing(Value array, Value index) {
3953 auto size = hw::type_cast<hw::ArrayType>(array.getType()).getNumElements();
3958 if (!llvm::isPowerOf2_64(
size)) {
3959 auto extElem = getOrCreateIntConstant(APInt(llvm::Log2_64_Ceil(
size), 0));
3960 auto extValue =
builder.create<hw::ArrayGetOp>(array, extElem);
3961 SmallVector<Value> temp(llvm::NextPowerOf2(
size) -
size, extValue);
3962 auto ext =
builder.create<hw::ArrayCreateOp>(temp);
3963 Value temp2[] = {ext.getResult(), array};
3964 array =
builder.create<hw::ArrayConcatOp>(temp2);
3967 Value inBoundsRead =
builder.create<hw::ArrayGetOp>(array, index);
3969 return inBoundsRead;
3972 LogicalResult FIRRTLLowering::visitExpr(MultibitMuxOp op) {
3974 auto index = getLoweredAndExtOrTruncValue(
3981 SmallVector<Value> loweredInputs;
3982 loweredInputs.reserve(op.getInputs().size());
3983 for (
auto input : op.getInputs()) {
3984 auto lowered = getLoweredAndExtendedValue(input, op.getType());
3987 loweredInputs.push_back(lowered);
3990 Value array =
builder.create<hw::ArrayCreateOp>(loweredInputs);
3991 return setLowering(op, createArrayIndexing(array, index));
3994 LogicalResult FIRRTLLowering::visitExpr(VerbatimExprOp op) {
3995 auto resultTy =
lowerType(op.getType());
3999 SmallVector<Value, 4> operands;
4000 operands.reserve(op.getSubstitutions().size());
4001 for (
auto operand : op.getSubstitutions()) {
4002 auto lowered = getLoweredValue(operand);
4005 operands.push_back(lowered);
4008 ArrayAttr symbols = op.getSymbolsAttr();
4012 return setLoweringTo<sv::VerbatimExprOp>(op, resultTy, op.getTextAttr(),
4016 LogicalResult FIRRTLLowering::visitExpr(XMRRefOp op) {
4020 Type baseType = op.getType().getType();
4023 if (isa<ClockType>(baseType))
4024 xmrType =
builder.getIntegerType(1);
4029 op.getRef(), op.getVerbatimSuffixAttr());
4032 LogicalResult FIRRTLLowering::visitExpr(XMRDerefOp op) {
4036 if (isa<ClockType>(op.getType()))
4037 xmrType =
builder.getIntegerType(1);
4041 auto xmr =
builder.create<sv::XMRRefOp>(
4043 auto readXmr = getReadValue(xmr);
4044 if (!isa<ClockType>(op.getType()))
4045 return setLowering(op, readXmr);
4046 return setLoweringTo<seq::ToClockOp>(op, readXmr);
4053 LogicalResult FIRRTLLowering::visitStmt(SkipOp op) {
4065 FailureOr<bool> FIRRTLLowering::lowerConnect(Value destVal, Value srcVal) {
4066 auto srcType = srcVal.getType();
4067 auto dstType = destVal.getType();
4068 if (srcType != dstType &&
4069 (isa<hw::TypeAliasType>(srcType) || isa<hw::TypeAliasType>(dstType))) {
4070 srcVal =
builder.create<hw::BitcastOp>(destVal.getType(), srcVal);
4072 return TypeSwitch<Operation *, FailureOr<bool>>(destVal.getDefiningOp())
4073 .Case<hw::WireOp>([&](
auto op) {
4074 maybeUnused(op.getInput());
4075 op.getInputMutable().assign(srcVal);
4078 .Case<seq::FirRegOp>([&](
auto op) {
4079 maybeUnused(op.getNext());
4080 op.getNextMutable().assign(srcVal);
4083 .Case<hw::StructExtractOp, hw::ArrayGetOp>([](
auto op) {
4086 op.emitOpError(
"used as connect destination");
4089 .Default([](
auto) {
return false; });
4092 LogicalResult FIRRTLLowering::visitStmt(ConnectOp op) {
4093 auto dest = op.getDest();
4095 auto destType = type_cast<FIRRTLBaseType>(dest.getType()).getPassiveType();
4096 auto srcVal = getLoweredAndExtendedValue(op.getSrc(), destType);
4098 return handleZeroBit(op.getSrc(), []() { return success(); });
4100 auto destVal = getPossiblyInoutLoweredValue(dest);
4104 auto result = lowerConnect(destVal, srcVal);
4112 if (updateIfBackedge(destVal, srcVal))
4116 return op.emitError(
"destination isn't an inout type");
4118 builder.create<sv::AssignOp>(destVal, srcVal);
4122 LogicalResult FIRRTLLowering::visitStmt(StrictConnectOp op) {
4123 auto dest = op.getDest();
4124 auto srcVal = getLoweredValue(op.getSrc());
4126 return handleZeroBit(op.getSrc(), []() { return success(); });
4128 auto destVal = getPossiblyInoutLoweredValue(dest);
4132 auto result = lowerConnect(destVal, srcVal);
4140 if (updateIfBackedge(destVal, srcVal))
4144 return op.emitError(
"destination isn't an inout type");
4146 builder.create<sv::AssignOp>(destVal, srcVal);
4150 LogicalResult FIRRTLLowering::visitStmt(ForceOp op) {
4151 auto srcVal = getLoweredValue(op.getSrc());
4155 auto destVal = getPossiblyInoutLoweredValue(op.getDest());
4160 return op.emitError(
"destination isn't an inout type");
4163 addToIfDefBlock(
"SYNTHESIS", std::function<
void()>(), [&]() {
4164 addToInitialBlock([&]() {
builder.create<sv::ForceOp>(destVal, srcVal); });
4169 LogicalResult FIRRTLLowering::visitStmt(RefForceOp op) {
4170 auto src = getLoweredNonClockValue(op.getSrc());
4171 auto clock = getLoweredNonClockValue(op.getClock());
4172 auto pred = getLoweredValue(op.getPredicate());
4173 if (!src || !clock || !pred)
4176 auto destVal = getPossiblyInoutLoweredValue(op.getDest());
4181 addToIfDefBlock(
"SYNTHESIS", std::function<
void()>(), [&]() {
4182 addToAlwaysBlock(clock, [&]() {
4183 addIfProceduralBlock(
4184 pred, [&]() {
builder.create<sv::ForceOp>(destVal, src); });
4189 LogicalResult FIRRTLLowering::visitStmt(RefForceInitialOp op) {
4190 auto src = getLoweredNonClockValue(op.getSrc());
4191 auto pred = getLoweredValue(op.getPredicate());
4195 auto destVal = getPossiblyInoutLoweredValue(op.getDest());
4200 addToIfDefBlock(
"SYNTHESIS", std::function<
void()>(), [&]() {
4201 addToInitialBlock([&]() {
4202 addIfProceduralBlock(
4203 pred, [&]() {
builder.create<sv::ForceOp>(destVal, src); });
4208 LogicalResult FIRRTLLowering::visitStmt(RefReleaseOp op) {
4209 auto clock = getLoweredNonClockValue(op.getClock());
4210 auto pred = getLoweredValue(op.getPredicate());
4211 if (!clock || !pred)
4214 auto destVal = getPossiblyInoutLoweredValue(op.getDest());
4219 addToIfDefBlock(
"SYNTHESIS", std::function<
void()>(), [&]() {
4220 addToAlwaysBlock(clock, [&]() {
4221 addIfProceduralBlock(pred,
4222 [&]() {
builder.create<sv::ReleaseOp>(destVal); });
4227 LogicalResult FIRRTLLowering::visitStmt(RefReleaseInitialOp op) {
4228 auto destVal = getPossiblyInoutLoweredValue(op.getDest());
4229 auto pred = getLoweredValue(op.getPredicate());
4230 if (!destVal || !pred)
4234 addToIfDefBlock(
"SYNTHESIS", std::function<
void()>(), [&]() {
4235 addToInitialBlock([&]() {
4236 addIfProceduralBlock(pred,
4237 [&]() {
builder.create<sv::ReleaseOp>(destVal); });
4245 LogicalResult FIRRTLLowering::visitStmt(PrintFOp op) {
4246 auto clock = getLoweredNonClockValue(op.getClock());
4247 auto cond = getLoweredValue(op.getCond());
4248 if (!clock || !cond)
4251 SmallVector<Value, 4> operands;
4252 operands.reserve(op.getSubstitutions().size());
4253 for (
auto operand : op.getSubstitutions()) {
4254 operands.push_back(getLoweredValue(operand));
4255 if (!operands.back()) {
4259 operands.back() = getOrCreateIntConstant(1, 0);
4264 addToIfDefBlock(
"SYNTHESIS", std::function<
void()>(), [&]() {
4265 addToAlwaysBlock(clock, [&]() {
4266 circuitState.used_PRINTF_COND =
true;
4270 builder.create<sv::MacroRefExprOp>(cond.getType(),
"PRINTF_COND_");
4271 ifCond =
builder.createOrFold<comb::AndOp>(ifCond, cond,
true);
4273 addIfProceduralBlock(ifCond, [&]() {
4275 Value fdStderr =
builder.create<hw::ConstantOp>(APInt(32, 0x80000002));
4276 builder.create<sv::FWriteOp>(fdStderr, op.getFormatString(), operands);
4286 LogicalResult FIRRTLLowering::visitStmt(StopOp op) {
4287 auto clock = getLoweredNonClockValue(op.getClock());
4288 auto cond = getLoweredValue(op.getCond());
4289 if (!clock || !cond)
4293 addToIfDefBlock(
"SYNTHESIS", std::function<
void()>(), [&]() {
4295 addToAlwaysBlock(clock, [&]() {
4296 circuitState.used_STOP_COND =
true;
4300 builder.create<sv::MacroRefExprOp>(cond.getType(),
"STOP_COND_");
4301 ifCond =
builder.createOrFold<comb::AndOp>(ifCond, cond,
true);
4302 addIfProceduralBlock(ifCond, [&]() {
4304 if (op.getExitCode())
4305 builder.create<sv::FatalOp>();
4307 builder.create<sv::FinishOp>();
4318 template <
typename... Args>
4320 StringRef opName, Args &&...args) {
4321 if (opName ==
"assert")
4322 return builder.create<sv::AssertOp>(std::forward<Args>(args)...);
4323 if (opName ==
"assume")
4324 return builder.create<sv::AssumeOp>(std::forward<Args>(args)...);
4325 if (opName ==
"cover")
4326 return builder.create<sv::CoverOp>(std::forward<Args>(args)...);
4327 llvm_unreachable(
"unknown verification op");
4333 template <
typename... Args>
4335 StringRef opName, Args &&...args) {
4336 if (opName ==
"assert")
4337 return builder.create<sv::AssertConcurrentOp>(std::forward<Args>(args)...);
4338 if (opName ==
"assume")
4339 return builder.create<sv::AssumeConcurrentOp>(std::forward<Args>(args)...);
4340 if (opName ==
"cover")
4341 return builder.create<sv::CoverConcurrentOp>(std::forward<Args>(args)...);
4342 llvm_unreachable(
"unknown verification op");
4363 LogicalResult FIRRTLLowering::lowerVerificationStatement(
4364 Operation *op, StringRef labelPrefix, Value opClock, Value opPredicate,
4365 Value opEnable, StringAttr opMessageAttr, ValueRange opOperands,
4366 StringAttr opNameAttr,
bool isConcurrent, EventControl opEventControl) {
4367 StringRef opName = op->getName().stripDialect();
4370 ArrayRef<Attribute> guards{};
4371 if (
auto guardsAttr = op->template getAttrOfType<ArrayAttr>(
"guards"))
4372 guards = guardsAttr.getValue();
4374 auto isAssert = opName ==
"assert";
4375 auto isCover = opName ==
"cover";
4381 auto isUnrOnlyAssert = llvm::any_of(guards, [](Attribute attr) {
4382 StringAttr strAttr = dyn_cast<StringAttr>(attr);
4383 return strAttr && strAttr.getValue() ==
"USE_UNR_ONLY_CONSTRAINTS";
4386 auto clock = getLoweredNonClockValue(opClock);
4387 auto enable = getLoweredValue(opEnable);
4388 auto predicate = getLoweredValue(opPredicate);
4389 if (!clock || !enable || !predicate)
4393 if (opNameAttr && !opNameAttr.getValue().empty())
4395 StringAttr prefixedLabel;
4401 SmallVector<Value> messageOps;
4402 if (!isCover && opMessageAttr && !opMessageAttr.getValue().empty()) {
4403 message = opMessageAttr;
4404 for (
auto operand : opOperands) {
4405 auto loweredValue = getLoweredValue(operand);
4406 if (!loweredValue) {
4410 loweredValue = getOrCreateIntConstant(1, 0);
4417 auto format = op->getAttrOfType<StringAttr>(
"format");
4418 if (isConcurrent && (!format || format.getValue() !=
"ifElseFatal" ||
4419 circuitState.emitChiselAssertsAsSVA))
4420 loweredValue =
builder.create<sv::SampledOp>(loweredValue);
4421 messageOps.push_back(loweredValue);
4427 if (!isConcurrent && !circuitState.emitChiselAssertsAsSVA) {
4429 builder.getContext(), circt::sv::DeferAssert::Immediate);
4430 addToAlwaysBlock(clock, [&]() {
4431 addIfProceduralBlock(enable, [&]() {
4433 prefixedLabel, message, messageOps);
4445 auto format = op->template getAttrOfType<StringAttr>(
"format");
4446 if (format && (format.getValue() ==
"ifElseFatal" &&
4447 !circuitState.emitChiselAssertsAsSVA)) {
4449 predicate =
builder.createOrFold<comb::AndOp>(enable, predicate,
true);
4450 addToIfDefBlock(
"SYNTHESIS", {}, [&]() {
4451 addToAlwaysBlock(clock, [&]() {
4452 addIfProceduralBlock(predicate, [&]() {
4453 circuitState.used_ASSERT_VERBOSE_COND =
true;
4454 circuitState.used_STOP_COND =
true;
4455 addIfProceduralBlock(
4456 builder.create<sv::MacroRefExprOp>(boolType,
4457 "ASSERT_VERBOSE_COND_"),
4458 [&]() { builder.create<sv::ErrorOp>(message, messageOps); });
4459 addIfProceduralBlock(
4460 builder.create<sv::MacroRefExprOp>(boolType,
"STOP_COND_"),
4461 [&]() { builder.create<sv::FatalOp>(); });
4473 predicate =
builder.createOrFold<comb::OrOp>(notEnable, predicate,
true);
4475 predicate =
builder.createOrFold<comb::AndOp>(enable, predicate,
true);
4479 sv::EventControl event;
4480 switch (opEventControl) {
4481 case EventControl::AtPosEdge:
4482 event = circt::sv::EventControl::AtPosEdge;
4484 case EventControl::AtEdge:
4485 event = circt::sv::EventControl::AtEdge;
4487 case EventControl::AtNegEdge:
4488 event = circt::sv::EventControl::AtNegEdge;
4495 predicate, prefixedLabel, message, messageOps);
4500 StringAttr assumeLabel;
4503 "assume__" + label.getValue());
4504 addToIfDefBlock(
"USE_PROPERTY_AS_CONSTRAINT", [&]() {
4505 if (!isUnrOnlyAssert) {
4506 builder.create<sv::AssumeConcurrentOp>(
4508 clock, predicate, assumeLabel);
4511 ArrayRef(sv::EventControl::AtEdge), ArrayRef(predicate), [&]() {
4515 circt::sv::DeferAssert::Immediate),
4526 bool anyFailed =
false;
4527 std::function<void()> emitWrapped = [&]() {
4528 if (guards.empty()) {
4532 auto guard = guards[0].dyn_cast<StringAttr>();
4534 op->emitOpError(
"elements in `guards` array must be `StringAttr`");
4538 guards = guards.drop_front();
4539 addToIfDefBlock(guard.getValue(), emitWrapped);
4548 LogicalResult FIRRTLLowering::visitStmt(AssertOp op) {
4549 return lowerVerificationStatement(
4550 op,
"assert__", op.getClock(), op.getPredicate(), op.getEnable(),
4551 op.getMessageAttr(), op.getSubstitutions(), op.getNameAttr(),
4552 op.getIsConcurrent(), op.getEventControl());
4556 LogicalResult FIRRTLLowering::visitStmt(AssumeOp op) {
4557 return lowerVerificationStatement(
4558 op,
"assume__", op.getClock(), op.getPredicate(), op.getEnable(),
4559 op.getMessageAttr(), op.getSubstitutions(), op.getNameAttr(),
4560 op.getIsConcurrent(), op.getEventControl());
4564 LogicalResult FIRRTLLowering::visitStmt(CoverOp op) {
4565 return lowerVerificationStatement(
4566 op,
"cover__", op.getClock(), op.getPredicate(), op.getEnable(),
4567 op.getMessageAttr(), op.getSubstitutions(), op.getNameAttr(),
4568 op.getIsConcurrent(), op.getEventControl());
4571 LogicalResult FIRRTLLowering::visitStmt(AttachOp op) {
4573 if (op.getAttached().size() < 2)
4576 SmallVector<Value, 4> inoutValues;
4577 for (
auto v : op.getAttached()) {
4578 inoutValues.push_back(getPossiblyInoutLoweredValue(v));
4579 if (!inoutValues.back()) {
4583 inoutValues.pop_back();
4588 return op.emitError(
"operand isn't an inout type");
4591 if (inoutValues.size() < 2)
4602 bool isAttachInternalOnly =
4603 llvm::none_of(inoutValues, [](
auto v) {
return isa<BlockArgument>(v); });
4605 if (isAttachInternalOnly) {
4606 auto v0 = inoutValues.front();
4607 for (
auto v : inoutValues) {
4610 v.replaceAllUsesWith(v0);
4621 SmallVector<Value, 4> values;
4622 for (
size_t i = 0, e = inoutValues.size(); i != e; ++i)
4623 values.push_back(getReadValue(inoutValues[i]));
4625 for (
size_t i1 = 0, e = inoutValues.size(); i1 != e; ++i1) {
4626 for (size_t i2 = 0; i2 != e; ++i2)
4628 builder.create<sv::AssignOp>(inoutValues[i1], values[i2]);
4637 builder.create<sv::VerbatimOp>(
4638 "`error \"Verilator does not support alias and thus "
4640 "arbitrarily connect bidirectional wires and ports\"");
4642 [&]() {
builder.create<sv::AliasOp>(inoutValues); });
4648 LogicalResult FIRRTLLowering::fixupLTLOps() {
4649 if (ltlOpFixupWorklist.empty())
4651 LLVM_DEBUG(
llvm::dbgs() <<
"Fixing up " << ltlOpFixupWorklist.size()
4655 for (
unsigned i = 0, e = ltlOpFixupWorklist.size(); i != e; ++i)
4656 for (
auto *user : ltlOpFixupWorklist[i]->getUsers())
4657 if (isa<hw::WireOp>(user))
4658 ltlOpFixupWorklist.insert(user);
4661 while (!ltlOpFixupWorklist.empty()) {
4662 auto *op = ltlOpFixupWorklist.pop_back_val();
4665 if (
auto opIntf = dyn_cast_or_null<mlir::InferTypeOpInterface>(op)) {
4666 LLVM_DEBUG(
llvm::dbgs() <<
"- Update " << *op <<
"\n");
4667 SmallVector<Type, 2> types;
4668 auto result = opIntf.inferReturnTypes(
4669 op->getContext(), op->getLoc(), op->getOperands(),
4670 op->getAttrDictionary(), op->getPropertiesStorage(), op->getRegions(),
4674 assert(types.size() == op->getNumResults());
4678 for (
auto [result, type] : llvm::zip(op->getResults(), types)) {
4679 if (result.getType() == type)
4682 <<
" - Result #" << result.getResultNumber() <<
" from "
4683 << result.getType() <<
" to " << type <<
"\n");
4684 result.setType(type);
4685 for (
auto *user : result.getUsers())
4687 ltlOpFixupWorklist.insert(user);
4692 if (
auto wireOp = dyn_cast<hw::WireOp>(op)) {
4693 if (isa<ltl::SequenceType, ltl::PropertyType>(wireOp.getType())) {
4694 wireOp.replaceAllUsesWith(wireOp.getInput());
4695 LLVM_DEBUG(
llvm::dbgs() <<
"- Remove " << wireOp <<
"\n");
4696 if (wireOp.use_empty())
4703 SmallPtrSet<Operation *, 4> usersReported;
4704 for (
auto *user : op->getUsers()) {
4705 if (!usersReported.insert(user).second)
4707 if (isa<ltl::LTLDialect, verif::VerifDialect>(user->getDialect()))
4709 if (isa<hw::WireOp>(user))
4711 auto d = op->emitError(
4712 "verification operation used in a non-verification context");
4713 d.attachNote(user->getLoc())
4714 <<
"leaking outside verification context here";
assert(baseType &&"element must be base type")
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 Operation * buildImmediateVerifOp(ImplicitLocOpBuilder &builder, StringRef opName, Args &&...args)
Helper function to build an immediate assert operation based on the original FIRRTL operation name.
static LogicalResult handleZeroBit(Value failedOperand, std::function< LogicalResult()> fn)
Zero bit operands end up looking like failures from getLoweredValue.
static Value castToFIRRTLType(Value val, Type type, ImplicitLocOpBuilder &builder)
Cast a value to a desired target type.
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 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 const char moduleHierarchyFileAttrName[]
Attribute that indicates that the module hierarchy starting at the annotated module should be dumped ...
static SmallVector< SubfieldOp > getAllFieldAccesses(Value structValue, StringRef field)
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 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 Value getSingleNonInstanceOperand(AttachOp op)
static IntType getWidestIntType(Type t1, Type t2)
Given two FIRRTL integer types, return the widest one.
llvm::SmallVector< StringAttr > outputs
static int64_t size(hw::ArrayType mType, capnp::schema::Field::Reader cField)
Returns the expected size of an array (capnp list) in 64-bit words.
Instantiate one of these and use it to build typed backedges.
void abandon()
Abandon the backedges, suppressing any diagnostics if they are still active upon destruction of the b...
Backedge get(mlir::Type resultType, mlir::LocationAttr optionalLoc={})
Create a typed backedge.
mlir::LogicalResult clearOrEmitError()
Clear the backedges, erasing any remaining cursor ops.
Backedge is a wrapper class around a Value.
A namespace that is used to store existing names and generate new names in some scope within the IR.
This class provides a read-only projection over the MLIR attributes that represent a set of annotatio...
bool removeAnnotations(llvm::function_ref< bool(Annotation)> predicate)
Remove all annotations from this annotation set for which predicate returns true.
bool removeAnnotation(Annotation anno)
Remove an annotation from this annotation set.
Annotation getAnnotation(StringRef className) const
If this annotation set has an annotation with the specified class name, return it.
This class provides a read-only projection of an annotation.
DictionaryAttr getDict() const
Get the data dictionary of this attribute.
AttrClass getMember(StringAttr name) const
Return a member of the annotation.
bool isClass(Args... names) const
Return true if this annotation matches any of the specified class names.
FIRRTLVisitor allows you to visit all of the expr/stmt/decls with one class declaration.
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.
Direction get(bool isOutput)
Returns an output direction if isOutput is true, otherwise returns an input direction.
Value createOrFoldNot(Location loc, Value value, OpBuilder &builder, bool twoState=false)
Create a `‘Not’' gate on a value.
Value createOrFoldSExt(Location loc, Value value, Type destTy, OpBuilder &builder)
Create a sign extension operation from a value of integer type to an equal or larger integer type.
constexpr const char * extractCoverageAnnoClass
constexpr const char * elaborationArtefactsDirectoryAnnoClass
FIRRTLBaseType getBaseType(Type type)
If it is a base type, return it as is.
constexpr const char * extractGrandCentralClass
constexpr const char * blackBoxAnnoClass
constexpr const char * dontObfuscateModuleAnnoClass
constexpr const char * metadataDirectoryAttrName
constexpr const char * testBenchDirAnnoClass
std::pair< hw::InnerSymAttr, StringAttr > getOrAddInnerSym(MLIRContext *context, hw::InnerSymAttr attr, uint64_t fieldID, llvm::function_ref< hw::InnerSymbolNamespace &()> getNamespace)
Ensure that the the InnerSymAttr has a symbol on the field specified.
constexpr const char * dutAnnoClass
constexpr const char * forceNameAnnoClass
constexpr const char * noDedupAnnoClass
mlir::Type getPassiveType(mlir::Type anyBaseFIRRTLType)
constexpr const char * subCircuitsTargetDirectoryAnnoClass
bool hasDroppableName(Operation *op)
Return true if the name is droppable.
constexpr const char * extractAssertAnnoClass
constexpr const char * verifBlackBoxAnnoClass
constexpr const char * blackBoxTargetDirAnnoClass
Type lowerType(Type type, std::optional< Location > loc={}, llvm::function_ref< hw::TypeAliasType(Type, BaseTypeAliasType, Location)> getTypeDeclFn={})
Given a type, return the corresponding lowered type for the HW dialect.
bool isExpression(Operation *op)
Return true if the specified operation is a firrtl expression.
constexpr const char * extractAssumeAnnoClass
constexpr const char * scalaClassAnnoClass
constexpr const char * testHarnessHierAnnoClass
constexpr const char * moduleHierAnnoClass
std::optional< int64_t > getBitWidth(FIRRTLBaseType type, bool ignoreFlip=false)
mlir::ArrayAttr getSVAttributes(mlir::Operation *op)
Return all the SV attributes of an operation, or null if there are none.
circt::hw::InOutType InOutType
void setSVAttributes(mlir::Operation *op, mlir::ArrayAttr attrs)
Set the SV attributes of an operation.
This file defines an intermediate representation for circuits acting as an abstraction for constraint...
std::unique_ptr< mlir::Pass > createLowerFIRRTLToHWPass(bool enableAnnotationWarning=false, bool emitChiselAssertsAsSVA=false, bool disableMemRandomization=false, bool disableRegRandomization=false)
This is the pass constructor.
mlir::raw_indented_ostream & dbgs()
def reg(value, clock, reset=None, reset_value=None, name=None, sym_name=None)