21 #include "mlir/IR/BuiltinOps.h"
22 #include "mlir/Tools/mlir-translate/Translation.h"
23 #include "llvm/ADT/APSInt.h"
24 #include "llvm/ADT/StringSet.h"
25 #include "llvm/ADT/TypeSwitch.h"
26 #include "llvm/Support/Debug.h"
28 #define DEBUG_TYPE "export-firrtl"
30 using namespace circt;
31 using namespace firrtl;
32 using namespace chirrtl;
33 using namespace pretty;
42 constexpr
size_t defaultTargetLineLength = 80;
46 Emitter(llvm::raw_ostream &os,
FIRVersion version,
47 size_t targetLineLength = defaultTargetLineLength)
48 : pp(os, targetLineLength), ps(pp, saver), version(version) {
49 pp.setListener(&saver);
51 LogicalResult finalize();
54 void emitCircuit(CircuitOp op);
55 void emitModule(FModuleOp op);
56 void emitModule(FExtModuleOp op);
57 void emitModule(FIntModuleOp op);
58 void emitModulePorts(ArrayRef<PortInfo> ports,
59 Block::BlockArgListType arguments = {});
60 void emitModuleParameters(Operation *op, ArrayAttr parameters);
61 void emitDeclaration(LayerOp op);
62 void emitDeclaration(OptionOp op);
63 void emitEnabledLayers(ArrayRef<Attribute> layers);
65 void emitParamAssign(ParamDeclAttr param, Operation *op,
66 std::optional<PPExtString> wordBeforeLHS = std::nullopt);
67 void emitGenericIntrinsic(GenericIntrinsicOp op);
70 void emitStatementsInBlock(
Block &block);
71 void emitStatement(WhenOp op);
72 void emitStatement(WireOp op);
73 void emitStatement(RegOp op);
74 void emitStatement(RegResetOp op);
75 void emitStatement(NodeOp op);
76 void emitStatement(StopOp op);
77 void emitStatement(SkipOp op);
78 void emitStatement(PrintFOp op);
79 void emitStatement(ConnectOp op);
80 void emitStatement(MatchingConnectOp op);
81 void emitStatement(PropAssignOp op);
82 void emitStatement(InstanceOp op);
83 void emitStatement(InstanceChoiceOp op);
84 void emitStatement(AttachOp op);
85 void emitStatement(MemOp op);
86 void emitStatement(InvalidValueOp op);
87 void emitStatement(CombMemOp op);
88 void emitStatement(SeqMemOp op);
89 void emitStatement(MemoryPortOp op);
90 void emitStatement(MemoryDebugPortOp op);
91 void emitStatement(MemoryPortAccessOp op);
92 void emitStatement(RefDefineOp op);
93 void emitStatement(RefForceOp op);
94 void emitStatement(RefForceInitialOp op);
95 void emitStatement(RefReleaseOp op);
96 void emitStatement(RefReleaseInitialOp op);
97 void emitStatement(LayerBlockOp op);
98 void emitStatement(GenericIntrinsicOp op);
101 void emitVerifStatement(T op, StringRef mnemonic);
102 void emitStatement(AssertOp op) { emitVerifStatement(op,
"assert"); }
103 void emitStatement(AssumeOp op) { emitVerifStatement(op,
"assume"); }
104 void emitStatement(CoverOp op) { emitVerifStatement(op,
"cover"); }
107 void emitExpression(Value value);
108 void emitExpression(ConstantOp op);
109 void emitExpression(SpecialConstantOp op);
110 void emitExpression(SubfieldOp op);
111 void emitExpression(SubindexOp op);
112 void emitExpression(SubaccessOp op);
113 void emitExpression(OpenSubfieldOp op);
114 void emitExpression(OpenSubindexOp op);
115 void emitExpression(RefResolveOp op);
116 void emitExpression(RefSendOp op);
117 void emitExpression(RefSubOp op);
118 void emitExpression(RWProbeOp op);
119 void emitExpression(RefCastOp op);
120 void emitExpression(UninferredResetCastOp op);
121 void emitExpression(ConstCastOp op);
122 void emitExpression(StringConstantOp op);
123 void emitExpression(FIntegerConstantOp op);
124 void emitExpression(BoolConstantOp op);
125 void emitExpression(DoubleConstantOp op);
126 void emitExpression(ListCreateOp op);
127 void emitExpression(UnresolvedPathOp op);
128 void emitExpression(GenericIntrinsicOp op);
130 void emitPrimExpr(StringRef mnemonic, Operation *op,
131 ArrayRef<uint32_t> attrs = {});
133 void emitExpression(BitsPrimOp op) {
134 emitPrimExpr(
"bits", op, {op.getHi(), op.getLo()});
136 void emitExpression(HeadPrimOp op) {
137 emitPrimExpr(
"head", op, op.getAmount());
139 void emitExpression(TailPrimOp op) {
140 emitPrimExpr(
"tail", op, op.getAmount());
142 void emitExpression(PadPrimOp op) { emitPrimExpr(
"pad", op, op.getAmount()); }
143 void emitExpression(ShlPrimOp op) { emitPrimExpr(
"shl", op, op.getAmount()); }
144 void emitExpression(ShrPrimOp op) { emitPrimExpr(
"shr", op, op.getAmount()); }
147 #define HANDLE(OPTYPE, MNEMONIC) \
148 void emitExpression(OPTYPE op) { emitPrimExpr(MNEMONIC, op); }
164 HANDLE(DShlPrimOp,
"dshl");
165 HANDLE(DShlwPrimOp,
"dshlw");
166 HANDLE(DShrPrimOp,
"dshr");
168 HANDLE(AsSIntPrimOp,
"asSInt");
169 HANDLE(AsUIntPrimOp,
"asUInt");
170 HANDLE(AsAsyncResetPrimOp,
"asAsyncReset");
171 HANDLE(AsClockPrimOp,
"asClock");
175 HANDLE(AndRPrimOp,
"andr");
177 HANDLE(XorRPrimOp,
"xorr");
181 void emitAttribute(MemDirAttr attr);
182 void emitAttribute(RUWAttr attr);
185 void emitType(Type type,
bool includeConst =
true);
186 void emitTypeWithColon(Type type) {
187 ps << PP::space <<
":" << PP::nbsp;
192 void emitLocation(Location loc);
193 void emitLocation(Operation *op) { emitLocation(op->getLoc()); }
194 template <
typename... Args>
195 void emitLocationAndNewLine(Args... args) {
198 ps << PP::neverbreak;
199 emitLocation(args...);
203 void emitAssignLike(llvm::function_ref<
void()> emitLHS,
204 llvm::function_ref<
void()> emitRHS,
206 std::optional<PPExtString> wordBeforeLHS = std::nullopt) {
208 ps.scopedBox(PP::ibox2, [&]() {
210 ps << *wordBeforeLHS << PP::space;
214 ps << PP::space << syntax << PP::nbsp;
216 ps.scopedBox(PP::ibox0, [&]() { emitRHS(); });
221 void emitSubExprIBox2(Value v) {
222 ps.scopedBox(PP::ibox2, [&]() { emitExpression(v); });
227 template <
typename Container,
typename EachFn>
228 void interleaveComma(
const Container &c, EachFn eachFn) {
229 llvm::interleave(c, eachFn, [&]() { ps <<
"," << PP::space; });
234 void interleaveComma(ValueRange ops) {
235 return interleaveComma(ops, [&](Value v) { emitSubExprIBox2(v); });
238 void emitStatementFunctionOp(
PPExtString name, Operation *op) {
241 ps.scopedBox(PP::ibox0, [&]() {
242 interleaveComma(op->getOperands());
245 emitLocationAndNewLine(op);
248 template <
typename EachFn,
typename Range>
249 void emitLiteralExpression(Type type,
const Range &r, EachFn eachFn) {
252 ps.scopedBox(PP::ibox0, [&]() {
253 interleaveComma(r, eachFn);
258 void emitLiteralExpression(Type type, ValueRange values) {
259 return emitLiteralExpression(type, values,
260 [&](Value v) { emitSubExprIBox2(v); });
264 void emitSymbol(SymbolRefAttr symbol) {
265 ps.ibox(2, IndentStyle::Block);
266 ps << symbol.getRootReference();
267 for (
auto nested : symbol.getNestedReferences()) {
270 ps << nested.getAttr();
277 InFlightDiagnostic emitError(Operation *op,
const Twine &message) {
278 encounteredError =
true;
279 return op->emitError(message);
283 InFlightDiagnostic emitOpError(Operation *op,
const Twine &message) {
284 encounteredError =
true;
285 return op->emitOpError(message);
290 std::optional<StringRef> lookupEmittedName(Value value) {
291 auto it = valueNames.find(value);
292 if (it != valueNames.end())
299 void emitPendingNewlineIfNeeded() {
300 if (pendingNewline) {
301 pendingNewline =
false;
305 void setPendingNewline() {
307 pendingNewline =
true;
310 void startStatement() { emitPendingNewlineIfNeeded(); }
328 bool pendingNewline =
false;
331 bool encounteredError =
false;
336 DenseMap<Value, StringRef> valueNames;
337 StringSet<> valueNamesStorage;
341 StringAttr legalize(StringAttr attr) {
342 StringRef str = attr.getValue();
343 if (str.empty() || !
isdigit(str.front()))
348 void addValueName(Value value, StringAttr attr) {
349 valueNames.insert({value, attr.getValue()});
351 void addValueName(Value value, StringRef str) {
352 auto it = valueNamesStorage.insert(str);
353 valueNames.insert({value, it.first->getKey()});
355 void addForceable(Forceable op, StringAttr attr) {
356 addValueName(op.getData(), attr);
357 if (op.isForceable()) {
358 SmallString<32> rwName;
359 (Twine(
"rwprobe(") + attr.strref() +
")").
toVector(rwName);
360 addValueName(op.getDataRef(), rwName);
369 SymbolTable symbolTable;
370 hw::InnerSymbolTableCollection istc;
371 hw::InnerRefNamespace irn{symbolTable, istc};
372 SymInfos(Operation *op) : symbolTable(op), istc(op){};
374 std::optional<std::reference_wrapper<SymInfos>> symInfos;
381 LogicalResult Emitter::finalize() {
return failure(encounteredError); }
384 void Emitter::emitCircuit(CircuitOp op) {
385 circuitNamespace.add(op);
386 SymInfos circuitSymInfos(op);
387 symInfos = circuitSymInfos;
389 ps <<
"FIRRTL version ";
396 ps <<
"circuit " <<
PPExtString(legalize(op.getNameAttr())) <<
" :";
399 for (
auto &bodyOp : *op.getBodyBlock()) {
400 if (encounteredError)
402 TypeSwitch<Operation *>(&bodyOp)
403 .Case<FModuleOp, FExtModuleOp, FIntModuleOp>([&](auto op) {
407 .Case<LayerOp>([&](
auto op) { emitDeclaration(op); })
408 .Case<OptionOp>([&](
auto op) { emitDeclaration(op); })
409 .Default([&](
auto op) {
410 emitOpError(op,
"not supported for emission inside circuit");
414 circuitNamespace.clear();
415 symInfos = std::nullopt;
418 void Emitter::emitEnabledLayers(ArrayRef<Attribute> layers) {
419 for (
auto layer : layers) {
421 ps.
cbox(2, IndentStyle::Block);
422 ps <<
"enablelayer" << PP::space;
423 emitSymbol(cast<SymbolRefAttr>(layer));
428 void Emitter::emitParamAssign(ParamDeclAttr param, Operation *op,
429 std::optional<PPExtString> wordBeforeLHS) {
431 ps << *wordBeforeLHS << PP::nbsp;
433 ps <<
PPExtString(param.getName().strref()) << PP::nbsp <<
"=" << PP::nbsp;
434 TypeSwitch<Attribute>(param.getValue())
435 .Case<IntegerAttr>([&](
auto attr) { ps.
addAsString(attr.getValue()); })
436 .Case<FloatAttr>([&](
auto attr) {
438 attr.getValue().toString(str);
443 .Default([&](
auto attr) {
444 emitOpError(op,
"with unsupported parameter attribute: ") << attr;
445 ps <<
"<unsupported-attr ";
451 void Emitter::emitGenericIntrinsic(GenericIntrinsicOp op) {
455 ps << op.getIntrinsic();
457 auto params = op.getParameters();
458 if (!params.empty()) {
460 ps.scopedBox(PP::ibox0, [&]() {
462 params.getAsRange<ParamDeclAttr>(),
463 [&](ParamDeclAttr param) { emitParamAssign(param, op); });
468 if (op.getNumResults() != 0)
469 emitTypeWithColon(op.getResult().getType());
471 if (op.getNumOperands() != 0) {
472 ps <<
"," << PP::space;
473 ps.
scopedBox(PP::ibox0, [&]() { interleaveComma(op->getOperands()); });
480 void Emitter::emitModule(FModuleOp op) {
482 ps.
cbox(4, IndentStyle::Block);
484 ps <<
"public" << PP::nbsp;
485 ps <<
"module " <<
PPExtString(legalize(op.getNameAttr()));
486 emitEnabledLayers(op.getLayers());
487 ps << PP::nbsp <<
":" << PP::end;
494 auto ports = op.getPorts();
495 emitModulePorts(ports, op.getArguments());
496 if (!ports.empty() && !op.getBodyBlock()->empty())
500 emitStatementsInBlock(*op.getBodyBlock());
503 valueNamesStorage.clear();
507 void Emitter::emitModule(FExtModuleOp op) {
509 ps.
cbox(4, IndentStyle::Block);
510 ps <<
"extmodule " <<
PPExtString(legalize(op.getNameAttr()));
511 emitEnabledLayers(op.getLayers());
512 ps << PP::nbsp <<
":" << PP::end;
519 auto ports = op.getPorts();
520 emitModulePorts(ports);
523 if (op.getDefname() && !op.getDefname()->empty()) {
525 ps <<
"defname = " <<
PPExtString(*op.getDefname());
530 emitModuleParameters(op, op.getParameters());
535 void Emitter::emitModule(FIntModuleOp op) {
537 ps.
cbox(4, IndentStyle::Block);
538 ps <<
"intmodule " <<
PPExtString(legalize(op.getNameAttr()));
539 emitEnabledLayers(op.getLayers());
540 ps << PP::nbsp <<
":" << PP::end;
547 auto ports = op.getPorts();
548 emitModulePorts(ports);
551 ps <<
"intrinsic = " <<
PPExtString(op.getIntrinsic());
555 emitModuleParameters(op, op.getParameters());
562 void Emitter::emitModulePorts(ArrayRef<PortInfo> ports,
563 Block::BlockArgListType arguments) {
564 for (
unsigned i = 0, e = ports.size(); i < e; ++i) {
566 const auto &port = ports[i];
567 ps << (port.direction ==
Direction::In ?
"input " :
"output ");
568 auto legalName = legalize(port.name);
569 if (!arguments.empty())
570 addValueName(arguments[i], legalName);
573 emitLocation(ports[i].loc);
578 void Emitter::emitModuleParameters(Operation *op, ArrayAttr parameters) {
579 for (
auto param : parameters.getAsRange<ParamDeclAttr>()) {
581 emitParamAssign(param, op,
PPExtString(
"parameter"));
587 void Emitter::emitDeclaration(LayerOp op) {
589 ps <<
"layer " <<
PPExtString(op.getSymName()) <<
", "
590 <<
PPExtString(stringifyLayerConvention(op.getConvention()));
592 if (
auto outputFile = op->getAttrOfType<hw::OutputFileAttr>(
"output_file")) {
598 emitLocationAndNewLine(op);
600 for (
auto &bodyOp : op.getBody().getOps()) {
601 TypeSwitch<Operation *>(&bodyOp)
602 .Case<LayerOp>([&](auto op) { emitDeclaration(op); })
603 .Default([&](
auto op) {
605 "not supported for emission inside layer definition");
612 void Emitter::emitDeclaration(OptionOp op) {
614 ps <<
"option " <<
PPExtString(legalize(op.getSymNameAttr())) <<
" :";
617 for (
auto caseOp : op.getBody().getOps<OptionCaseOp>()) {
619 ps << PPExtString(legalize(caseOp.getSymNameAttr()));
620 emitLocation(caseOp);
623 ps << PP::newline << PP::newline;
634 return (
isExpression(op) && !isa<InvalidValueOp>(op)) ||
635 (isa<GenericIntrinsicOp>(op) && op->hasOneUse() &&
639 void Emitter::emitStatementsInBlock(Block &block) {
640 for (
auto &bodyOp : block) {
641 if (encounteredError)
645 TypeSwitch<Operation *>(&bodyOp)
646 .Case<WhenOp, WireOp, RegOp, RegResetOp, NodeOp, StopOp, SkipOp,
647 PrintFOp, AssertOp, AssumeOp, CoverOp, ConnectOp,
648 MatchingConnectOp, PropAssignOp, InstanceOp, InstanceChoiceOp,
649 AttachOp, MemOp, InvalidValueOp, SeqMemOp, CombMemOp,
650 MemoryPortOp, MemoryDebugPortOp, MemoryPortAccessOp, RefDefineOp,
651 RefForceOp, RefForceInitialOp, RefReleaseOp, RefReleaseInitialOp,
652 LayerBlockOp, GenericIntrinsicOp>(
653 [&](
auto op) { emitStatement(op); })
654 .Default([&](
auto op) {
656 ps <<
"// operation " <<
PPExtString(op->getName().getStringRef());
658 emitOpError(op,
"not supported as statement");
663 void Emitter::emitStatement(WhenOp op) {
666 emitExpression(op.getCondition());
668 emitLocationAndNewLine(op);
669 ps.
scopedBox(PP::bbox2, [&]() { emitStatementsInBlock(op.getThenBlock()); });
671 if (!op.hasElseRegion())
678 auto &elseBlock = op.getElseBlock();
679 if (!elseBlock.empty() && &elseBlock.front() == &elseBlock.back()) {
680 if (
auto whenOp = dyn_cast<WhenOp>(&elseBlock.front())) {
681 emitStatement(whenOp);
688 ps.
scopedBox(PP::bbox2, [&]() { emitStatementsInBlock(elseBlock); });
691 void Emitter::emitStatement(WireOp op) {
692 auto legalName = legalize(op.getNameAttr());
693 addForceable(op, legalName);
697 emitTypeWithColon(op.getResult().getType());
699 emitLocationAndNewLine(op);
702 void Emitter::emitStatement(RegOp op) {
703 auto legalName = legalize(op.getNameAttr());
704 addForceable(op, legalName);
708 emitTypeWithColon(op.getResult().getType());
709 ps <<
"," << PP::space;
710 emitExpression(op.getClockVal());
712 emitLocationAndNewLine(op);
715 void Emitter::emitStatement(RegResetOp op) {
716 auto legalName = legalize(op.getNameAttr());
717 addForceable(op, legalName);
721 ps <<
"regreset " << legalName;
722 emitTypeWithColon(op.getResult().getType());
723 ps <<
"," << PP::space;
724 emitExpression(op.getClockVal());
725 ps <<
"," << PP::space;
726 emitExpression(op.getResetSignal());
727 ps <<
"," << PP::space;
728 emitExpression(op.getResetValue());
732 ps <<
"reg " << legalName;
733 emitTypeWithColon(op.getResult().getType());
734 ps <<
"," << PP::space;
735 emitExpression(op.getClockVal());
736 ps << PP::space <<
"with :";
738 ps << PP::neverbreak;
741 ps <<
"reset => (" << PP::ibox0;
742 emitExpression(op.getResetSignal());
743 ps <<
"," << PP::space;
744 emitExpression(op.getResetValue());
745 ps <<
")" << PP::end;
748 emitLocationAndNewLine(op);
751 void Emitter::emitStatement(NodeOp op) {
752 auto legalName = legalize(op.getNameAttr());
753 addForceable(op, legalName);
755 emitAssignLike([&]() { ps <<
"node " <<
PPExtString(legalName); },
756 [&]() { emitExpression(op.getInput()); });
757 emitLocationAndNewLine(op);
760 void Emitter::emitStatement(StopOp op) {
763 ps <<
"stop(" << PP::ibox0;
764 emitExpression(op.getClock());
765 ps <<
"," << PP::space;
766 emitExpression(op.getCond());
767 ps <<
"," << PP::space;
769 ps <<
")" << PP::end;
770 if (!op.getName().empty()) {
771 ps << PP::space <<
": " <<
PPExtString(legalize(op.getNameAttr()));
774 emitLocationAndNewLine(op);
777 void Emitter::emitStatement(SkipOp op) {
780 emitLocationAndNewLine(op);
783 void Emitter::emitStatement(PrintFOp op) {
786 ps <<
"printf(" << PP::ibox0;
787 emitExpression(op.getClock());
788 ps <<
"," << PP::space;
789 emitExpression(op.getCond());
790 ps <<
"," << PP::space;
792 for (
auto operand : op.getSubstitutions()) {
793 ps <<
"," << PP::space;
794 emitExpression(operand);
796 ps <<
")" << PP::end;
797 if (!op.getName().empty()) {
798 ps << PP::space <<
": " <<
PPExtString(legalize(op.getNameAttr()));
801 emitLocationAndNewLine(op);
805 void Emitter::emitVerifStatement(T op, StringRef mnemonic) {
808 ps << mnemonic <<
"(" << PP::ibox0;
809 emitExpression(op.getClock());
810 ps <<
"," << PP::space;
811 emitExpression(op.getPredicate());
812 ps <<
"," << PP::space;
813 emitExpression(op.getEnable());
814 ps <<
"," << PP::space;
816 ps <<
")" << PP::end;
817 if (!op.getName().empty()) {
818 ps << PP::space <<
": " <<
PPExtString(legalize(op.getNameAttr()));
821 emitLocationAndNewLine(op);
824 void Emitter::emitStatement(ConnectOp op) {
828 if (op.getSrc().getDefiningOp<InvalidValueOp>()) {
829 ps <<
"invalidate" << PP::space;
830 emitExpression(op.getDest());
832 ps <<
"connect" << PP::space;
833 emitExpression(op.getDest());
834 ps <<
"," << PP::space;
835 emitExpression(op.getSrc());
839 auto emitLHS = [&]() { emitExpression(op.getDest()); };
840 if (op.getSrc().getDefiningOp<InvalidValueOp>()) {
842 emitLHS, [&]() { ps <<
"invalid"; },
PPExtString(
"is"));
845 emitLHS, [&]() { emitExpression(op.getSrc()); },
PPExtString(
"<="));
848 emitLocationAndNewLine(op);
851 void Emitter::emitStatement(MatchingConnectOp op) {
855 if (op.getSrc().getDefiningOp<InvalidValueOp>()) {
856 ps <<
"invalidate" << PP::space;
857 emitExpression(op.getDest());
859 ps <<
"connect" << PP::space;
860 emitExpression(op.getDest());
861 ps <<
"," << PP::space;
862 emitExpression(op.getSrc());
866 auto emitLHS = [&]() { emitExpression(op.getDest()); };
867 if (op.getSrc().getDefiningOp<InvalidValueOp>()) {
869 emitLHS, [&]() { ps <<
"invalid"; },
PPExtString(
"is"));
872 emitLHS, [&]() { emitExpression(op.getSrc()); },
PPExtString(
"<="));
875 emitLocationAndNewLine(op);
878 void Emitter::emitStatement(PropAssignOp op) {
881 ps <<
"propassign" << PP::space;
882 interleaveComma(op.getOperands());
884 emitLocationAndNewLine(op);
887 void Emitter::emitStatement(InstanceOp op) {
889 auto legalName = legalize(op.getNameAttr());
891 <<
PPExtString(legalize(op.getModuleNameAttr().getAttr()));
892 emitLocationAndNewLine(op);
896 SmallString<16> portName(legalName);
897 portName.push_back(
'.');
898 unsigned baseLen = portName.size();
899 for (
unsigned i = 0, e = op.getNumResults(); i < e; ++i) {
900 portName.append(legalize(op.getPortName(i)));
901 addValueName(op.getResult(i), portName);
902 portName.resize(baseLen);
906 void Emitter::emitStatement(InstanceChoiceOp op) {
908 auto legalName = legalize(op.getNameAttr());
909 ps <<
"instchoice " <<
PPExtString(legalName) <<
" of "
910 <<
PPExtString(legalize(op.getDefaultTargetAttr().getAttr())) <<
", "
911 <<
PPExtString(legalize(op.getOptionNameAttr())) <<
" :";
914 for (
const auto &[optSym, targetSym] : op.getTargetChoices()) {
916 ps << PPExtString(legalize(optSym.getLeafReference()));
918 ps << PPExtString(legalize(targetSym.getAttr()));
923 SmallString<16> portName(legalName);
924 portName.push_back(
'.');
925 unsigned baseLen = portName.size();
926 for (
unsigned i = 0, e = op.getNumResults(); i < e; ++i) {
927 portName.append(legalize(op.getPortName(i)));
928 addValueName(op.getResult(i), portName);
929 portName.resize(baseLen);
933 void Emitter::emitStatement(AttachOp op) {
934 emitStatementFunctionOp(
PPExtString(
"attach"), op);
937 void Emitter::emitStatement(MemOp op) {
938 auto legalName = legalize(op.getNameAttr());
939 SmallString<16> portName(legalName);
940 portName.push_back(
'.');
941 auto portNameBaseLen = portName.size();
942 for (
auto result : llvm::zip(op.getResults(), op.getPortNames())) {
943 portName.resize(portNameBaseLen);
944 portName.append(legalize(cast<StringAttr>(std::get<1>(result))));
945 addValueName(std::get<0>(result), portName);
950 emitLocationAndNewLine(op);
953 ps <<
"data-type => ";
954 emitType(op.getDataType());
959 ps <<
"read-latency => ";
962 ps <<
"write-latency => ";
966 SmallString<16> reader, writer, readwriter;
967 for (std::pair<StringAttr, MemOp::PortKind> port : op.getPorts()) {
968 auto add = [&](SmallString<16> &to, StringAttr name) {
971 to.append(name.getValue());
973 switch (port.second) {
974 case MemOp::PortKind::Read:
975 add(reader, legalize(port.first));
977 case MemOp::PortKind::Write:
978 add(writer, legalize(port.first));
980 case MemOp::PortKind::ReadWrite:
981 add(readwriter, legalize(port.first));
983 case MemOp::PortKind::Debug:
984 emitOpError(op,
"has unsupported 'debug' port");
989 ps <<
"reader => " << reader << PP::newline;
991 ps <<
"writer => " << writer << PP::newline;
992 if (!readwriter.empty())
993 ps <<
"readwriter => " << readwriter << PP::newline;
995 ps <<
"read-under-write => ";
996 emitAttribute(op.getRuw());
1001 void Emitter::emitStatement(SeqMemOp op) {
1004 ps <<
"smem " <<
PPExtString(legalize(op.getNameAttr()));
1005 emitTypeWithColon(op.getType());
1006 ps <<
"," << PP::space;
1007 emitAttribute(op.getRuw());
1009 emitLocationAndNewLine(op);
1012 void Emitter::emitStatement(CombMemOp op) {
1015 ps <<
"cmem " <<
PPExtString(legalize(op.getNameAttr()));
1016 emitTypeWithColon(op.getType());
1018 emitLocationAndNewLine(op);
1021 void Emitter::emitStatement(MemoryPortOp op) {
1023 addValueName(op.getData(), legalize(op.getNameAttr()));
1026 void Emitter::emitStatement(MemoryDebugPortOp op) {
1028 addValueName(op.getData(), legalize(op.getNameAttr()));
1031 void Emitter::emitStatement(MemoryPortAccessOp op) {
1035 auto port = cast<MemoryPortOp>(op.getPort().getDefiningOp());
1036 emitAttribute(port.getDirection());
1038 ps <<
" mport " <<
PPExtString(legalize(port.getNameAttr())) <<
" = ";
1041 auto *mem = port.getMemory().getDefiningOp();
1042 if (
auto seqMem = dyn_cast<SeqMemOp>(mem))
1043 ps << legalize(seqMem.getNameAttr());
1045 ps << legalize(cast<CombMemOp>(mem).getNameAttr());
1049 emitExpression(op.getIndex());
1053 emitExpression(op.getClock());
1055 emitLocationAndNewLine(op);
1058 void Emitter::emitStatement(RefDefineOp op) {
1060 emitAssignLike([&]() { emitExpression(op.getDest()); },
1061 [&]() { emitExpression(op.getSrc()); },
PPExtString(
"="),
1063 emitLocationAndNewLine(op);
1066 void Emitter::emitStatement(RefForceOp op) {
1067 emitStatementFunctionOp(
PPExtString(
"force"), op);
1070 void Emitter::emitStatement(RefForceInitialOp op) {
1072 auto constantPredicate =
1073 dyn_cast_or_null<ConstantOp>(op.getPredicate().getDefiningOp());
1074 bool hasEnable = !constantPredicate || constantPredicate.getValue() == 0;
1077 emitExpression(op.getPredicate());
1078 ps <<
":" << PP::bbox2 << PP::neverbreak << PP::newline;
1080 ps <<
"force_initial(";
1082 interleaveComma({op.getDest(), op.getSrc()});
1087 emitLocationAndNewLine(op);
1090 void Emitter::emitStatement(RefReleaseOp op) {
1091 emitStatementFunctionOp(
PPExtString(
"release"), op);
1094 void Emitter::emitStatement(RefReleaseInitialOp op) {
1096 auto constantPredicate =
1097 dyn_cast_or_null<ConstantOp>(op.getPredicate().getDefiningOp());
1098 bool hasEnable = !constantPredicate || constantPredicate.getValue() == 0;
1101 emitExpression(op.getPredicate());
1102 ps <<
":" << PP::bbox2 << PP::neverbreak << PP::newline;
1104 ps <<
"release_initial(";
1105 emitExpression(op.getDest());
1109 emitLocationAndNewLine(op);
1112 void Emitter::emitStatement(LayerBlockOp op) {
1114 ps <<
"layerblock " << op.getLayerName().getLeafReference() <<
" :";
1115 emitLocationAndNewLine(op);
1116 auto *body = op.getBody();
1117 ps.
scopedBox(PP::bbox2, [&]() { emitStatementsInBlock(*body); });
1120 void Emitter::emitStatement(InvalidValueOp op) {
1123 if (llvm::all_of(op->getUses(), [&](OpOperand &use) {
1124 return use.getOperandNumber() == 1 &&
1125 isa<ConnectOp, MatchingConnectOp>(use.getOwner());
1131 auto name = circuitNamespace.newName(
"_invalid");
1132 addValueName(op, name);
1134 emitType(op.getType());
1135 emitLocationAndNewLine(op);
1141 emitLocationAndNewLine(op);
1144 void Emitter::emitStatement(GenericIntrinsicOp op) {
1147 emitGenericIntrinsic(op);
1150 auto name = circuitNamespace.newName(
"_gen_int");
1151 addValueName(op.getResult(), name);
1152 emitAssignLike([&]() { ps <<
"node " <<
PPExtString(name); },
1153 [&]() { emitGenericIntrinsic(op); });
1155 emitLocationAndNewLine(op);
1158 void Emitter::emitExpression(Value value) {
1161 if (
auto name = lookupEmittedName(value)) {
1167 auto op = value.getDefiningOp();
1168 assert(op &&
"value must either be a block arg or the result of an op");
1169 TypeSwitch<Operation *>(op)
1172 ConstantOp, SpecialConstantOp, SubfieldOp, SubindexOp, SubaccessOp,
1173 OpenSubfieldOp, OpenSubindexOp,
1175 AddPrimOp, SubPrimOp, MulPrimOp, DivPrimOp, RemPrimOp, AndPrimOp,
1176 OrPrimOp, XorPrimOp, LEQPrimOp, LTPrimOp, GEQPrimOp, GTPrimOp,
1177 EQPrimOp, NEQPrimOp, CatPrimOp, DShlPrimOp, DShlwPrimOp, DShrPrimOp,
1179 AsSIntPrimOp, AsUIntPrimOp, AsAsyncResetPrimOp, AsClockPrimOp,
1180 CvtPrimOp, NegPrimOp, NotPrimOp, AndRPrimOp, OrRPrimOp, XorRPrimOp,
1182 BitsPrimOp, HeadPrimOp, TailPrimOp, PadPrimOp, MuxPrimOp, ShlPrimOp,
1183 ShrPrimOp, UninferredResetCastOp, ConstCastOp, StringConstantOp,
1184 FIntegerConstantOp, BoolConstantOp, DoubleConstantOp, ListCreateOp,
1185 UnresolvedPathOp, GenericIntrinsicOp,
1187 RefSendOp, RefResolveOp, RefSubOp, RWProbeOp, RefCastOp>(
1189 ps.
scopedBox(PP::ibox0, [&]() { emitExpression(op); });
1191 .Default([&](
auto op) {
1192 emitOpError(op,
"not supported as expression");
1193 ps <<
"<unsupported-expr-" <<
PPExtString(op->getName().stripDialect())
1198 void Emitter::emitExpression(ConstantOp op) {
1200 emitType(op.getType(),
false);
1207 void Emitter::emitExpression(SpecialConstantOp op) {
1208 auto emitInner = [&]() {
1215 .
Case<ClockType>([&](
auto type) {
1220 .Case<ResetType>([&](
auto type) { emitInner(); })
1221 .Case<AsyncResetType>([&](
auto type) {
1222 ps <<
"asAsyncReset(";
1229 void Emitter::emitExpression(SubfieldOp op) {
1230 BundleType type = op.getInput().getType();
1231 emitExpression(op.getInput());
1232 ps <<
"." << legalize(type.getElementNameAttr(op.getFieldIndex()));
1236 void Emitter::emitExpression(SubindexOp op) {
1237 emitExpression(op.getInput());
1244 void Emitter::emitExpression(SubaccessOp op) {
1245 emitExpression(op.getInput());
1247 emitExpression(op.getIndex());
1251 void Emitter::emitExpression(OpenSubfieldOp op) {
1252 auto type = op.getInput().getType();
1253 emitExpression(op.getInput());
1254 ps <<
"." << legalize(type.getElementNameAttr(op.getFieldIndex()));
1257 void Emitter::emitExpression(OpenSubindexOp op) {
1258 emitExpression(op.getInput());
1264 void Emitter::emitExpression(RefSendOp op) {
1266 emitExpression(op.getBase());
1270 void Emitter::emitExpression(RefResolveOp op) {
1272 emitExpression(op.getRef());
1276 void Emitter::emitExpression(RefSubOp op) {
1277 emitExpression(op.getInput());
1279 .
Case<FVectorType>([&](
auto type) {
1285 [&](
auto type) { ps <<
"." << type.getElementName(op.getIndex()); });
1288 void Emitter::emitExpression(RWProbeOp op) {
1292 auto target = symInfos->get().irn.lookup(op.getTarget());
1294 if (target.isPort()) {
1295 auto mod = cast<FModuleOp>(target.getOp());
1296 auto port = target.getPort();
1297 base = mod.getArgument(port);
1299 base = cast<hw::InnerSymbolOpInterface>(target.getOp()).getTargetResult();
1302 emitExpression(base);
1305 auto fieldID = target.getField();
1306 auto type = base.getType();
1309 .
Case<FVectorType, OpenVectorType>([&](
auto vecTy) {
1310 auto index = vecTy.getIndexForFieldID(fieldID);
1314 auto [subtype, subfieldID] = vecTy.getSubTypeByFieldID(fieldID);
1316 fieldID = subfieldID;
1318 .Case<BundleType, OpenBundleType>([&](
auto bundleTy) {
1319 auto index = bundleTy.getIndexForFieldID(fieldID);
1320 ps <<
"." << bundleTy.getElementName(index);
1321 auto [subtype, subfieldID] = bundleTy.getSubTypeByFieldID(fieldID);
1323 fieldID = subfieldID;
1329 void Emitter::emitExpression(RefCastOp op) { emitExpression(op.getInput()); }
1331 void Emitter::emitExpression(UninferredResetCastOp op) {
1332 emitExpression(op.getInput());
1335 void Emitter::emitExpression(FIntegerConstantOp op) {
1341 void Emitter::emitExpression(BoolConstantOp op) {
1342 ps <<
"Bool(" << (op.getValue() ?
"true" :
"false") <<
")";
1345 void Emitter::emitExpression(DoubleConstantOp op) {
1351 SmallString<16> str;
1352 op.getValueAttr().getValue().toString(str);
1357 void Emitter::emitExpression(StringConstantOp op) {
1363 void Emitter::emitExpression(ListCreateOp op) {
1364 return emitLiteralExpression(op.getType(), op.getElements());
1367 void Emitter::emitExpression(UnresolvedPathOp op) {
1373 void Emitter::emitExpression(GenericIntrinsicOp op) {
1374 emitGenericIntrinsic(op);
1377 void Emitter::emitExpression(ConstCastOp op) { emitExpression(op.getInput()); }
1379 void Emitter::emitPrimExpr(StringRef mnemonic, Operation *op,
1380 ArrayRef<uint32_t> attrs) {
1381 ps << mnemonic <<
"(" << PP::ibox0;
1382 interleaveComma(op->getOperands());
1383 if (!op->getOperands().empty() && !attrs.empty())
1384 ps <<
"," << PP::space;
1385 interleaveComma(attrs, [&](
auto attr) { ps.
addAsString(attr); });
1386 ps <<
")" << PP::end;
1389 void Emitter::emitAttribute(MemDirAttr attr) {
1391 case MemDirAttr::Infer:
1394 case MemDirAttr::Read:
1397 case MemDirAttr::Write:
1400 case MemDirAttr::ReadWrite:
1406 void Emitter::emitAttribute(RUWAttr attr) {
1408 case RUWAttr::Undefined:
1421 void Emitter::emitType(Type type,
bool includeConst) {
1422 if (includeConst &&
isConst(type))
1424 auto emitWidth = [&](std::optional<int32_t>
width) {
1433 .
Case<ClockType>([&](
auto) { ps <<
"Clock"; })
1434 .Case<ResetType>([&](
auto) { ps <<
"Reset"; })
1435 .Case<AsyncResetType>([&](
auto) { ps <<
"AsyncReset"; })
1436 .Case<UIntType>([&](
auto type) {
1438 emitWidth(type.getWidth());
1440 .Case<SIntType>([&](
auto type) {
1442 emitWidth(type.getWidth());
1444 .Case<AnalogType>([&](
auto type) {
1446 emitWidth(type.getWidth());
1448 .Case<OpenBundleType, BundleType>([&](
auto type) {
1450 if (!type.getElements().empty())
1452 bool anyEmitted =
false;
1454 for (
auto &element : type.getElements()) {
1456 ps <<
"," << PP::space;
1457 ps.scopedBox(PP::ibox2, [&]() {
1460 ps << legalize(element.name);
1461 emitTypeWithColon(element.type);
1470 .Case<OpenVectorType, FVectorType, CMemoryType>([&](
auto type) {
1471 emitType(type.getElementType());
1476 .Case<RefType>([&](RefType type) {
1477 if (type.getForceable())
1480 ps.
cbox(2, IndentStyle::Block);
1482 emitType(type.getType());
1483 if (
auto layer = type.getLayer()) {
1486 emitSymbol(type.getLayer());
1491 .Case<AnyRefType>([&](AnyRefType type) { ps <<
"AnyRef"; })
1492 .Case<StringType>([&](StringType type) { ps <<
"String"; })
1493 .Case<FIntegerType>([&](FIntegerType type) { ps <<
"Integer"; })
1494 .Case<BoolType>([&](BoolType type) { ps <<
"Bool"; })
1495 .Case<DoubleType>([&](DoubleType type) { ps <<
"Double"; })
1496 .Case<PathType>([&](PathType type) { ps <<
"Path"; })
1497 .Case<ListType>([&](ListType type) {
1499 emitType(type.getElementType());
1502 .Default([&](
auto type) {
1503 llvm_unreachable(
"all types should be implemented");
1509 void Emitter::emitLocation(Location loc) {
1511 ps << PP::neverbreak;
1513 dyn_cast_or_null<FileLineColLoc, LocationAttr>(LocationAttr(loc))) {
1514 ps <<
" @[" << fileLoc.getFilename().getValue();
1515 if (
auto line = fileLoc.getLine()) {
1518 if (
auto col = fileLoc.getColumn()) {
1535 std::optional<size_t> targetLineLength,
1537 Emitter emitter(os, version,
1538 targetLineLength.value_or(defaultTargetLineLength));
1539 for (
auto &op : *module.getBody()) {
1540 if (
auto circuitOp = dyn_cast<CircuitOp>(op))
1541 emitter.emitCircuit(circuitOp);
1543 return emitter.finalize();
1548 "target-line-length",
1549 llvm::cl::desc(
"Target line length for emitted .fir"),
1550 llvm::cl::value_desc(
"number of chars"),
1551 llvm::cl::init(defaultTargetLineLength));
1552 static mlir::TranslateFromMLIRRegistration toFIR(
1553 "export-firrtl",
"emit FIRRTL dialect operations to .fir output",
1554 [](ModuleOp module, llvm::raw_ostream &os) {
1557 [](mlir::DialectRegistry ®istry) {
1558 registry.insert<chirrtl::CHIRRTLDialect>();
1559 registry.insert<firrtl::FIRRTLDialect>();
assert(baseType &&"element must be base type")
#define HANDLE(OPTYPE, MNEMONIC)
static bool isEmittedInline(Operation *op)
Check if an operation is inlined into the emission of their users.
static std::vector< mlir::Value > toVector(mlir::ValueRange range)
This class implements the same functionality as TypeSwitch except that it uses firrtl::type_dyn_cast ...
FIRRTLTypeSwitch< T, ResultT > & Case(CallableT &&caseFn)
Add a case on the given type.
void space()
Add a breakable space.
void cbox(int32_t offset=0, IndentStyle style=IndentStyle::Visual)
Start a consistent group with specified offset.
void zerobreak()
Add a break that is zero-wide if not broken.
Wrap a PrettyPrinter with TokenBuilder features as well as operator<<'s.
auto scopedBox(T &&t, Callable &&c, Token close=EndToken())
Open a box, invoke the lambda, and close it after.
TokenStream & writeQuotedEscaped(StringRef str, bool useHexEscapes=false, StringRef left="\"", StringRef right="\"")
TokenStream & addAsString(T &&t)
General-purpose "format this" helper, for types not supported by operator<< yet.
PrettyPrinter::Listener that saves strings while live.
Direction get(bool isOutput)
Returns an output direction if isOutput is true, otherwise returns an input direction.
mlir::LogicalResult exportFIRFile(mlir::ModuleOp module, llvm::raw_ostream &os, std::optional< size_t > targetLineLength, FIRVersion version)
constexpr FIRVersion exportFIRVersion(4, 0, 0)
void registerToFIRFileTranslation()
bool isConst(Type type)
Returns true if this is a 'const' type whose value is guaranteed to be unchanging at circuit executio...
bool isExpression(Operation *op)
Return true if the specified operation is a firrtl expression.
The InstanceGraph op interface, see InstanceGraphInterface.td for more details.
The namespace of a CircuitOp, generally inhabited by modules.
The FIRRTL specification version.
String wrapper to indicate string has external storage.