CIRCT  19.0.0git
ExportChiselInterface.cpp
Go to the documentation of this file.
1 //===- ExportChiselInterface.cpp - Chisel Interface Emitter ---------------===//
2 //
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 //
7 //===----------------------------------------------------------------------===//
8 //
9 // This is the Chisel interface emitter implementation.
10 //
11 //===----------------------------------------------------------------------===//
12 
14 #include "../PassDetail.h"
16 #include "circt/Support/Version.h"
17 #include "mlir/Support/FileUtilities.h"
18 #include "llvm/Support/FileSystem.h"
19 #include "llvm/Support/Path.h"
20 #include "llvm/Support/ToolOutputFile.h"
21 
22 using namespace circt;
23 using namespace firrtl;
24 
25 #define DEBUG_TYPE "export-chisel-package"
26 
27 //===----------------------------------------------------------------------===//
28 // Interface emission logic
29 //===----------------------------------------------------------------------===//
30 
31 static const unsigned int indentIncrement = 2;
32 
33 namespace {
34 class Emitter {
35 public:
36  Emitter(llvm::raw_ostream &os) : os(os) {}
37 
38  bool hasEmittedProbeType() { return hasEmittedProbe; }
39 
40  /// Emits an `ExtModule` class with port declarations for `module`.
41  LogicalResult emitModule(FModuleLike module) {
42  os << "class " << module.getModuleName() << " extends ExtModule {\n";
43 
44  for (const auto &port : module.getPorts()) {
45  if (failed(emitPort(port)))
46  return failure();
47  }
48 
49  os << "}\n";
50 
51  return success();
52  }
53 
54 private:
55  /// Emits an `IO` for the `port`.
56  LogicalResult emitPort(const PortInfo &port) {
57  os.indent(indentIncrement) << "val " << port.getName() << " = IO(";
58  if (failed(
59  emitPortType(port.loc, port.type, port.direction, indentIncrement)))
60  return failure();
61  os << ")\n";
62 
63  return success();
64  }
65 
66  /// Emits type construction expression for the port type, recursing into
67  /// aggregate types as needed.
68  LogicalResult emitPortType(Location location, Type type, Direction direction,
69  unsigned int indent,
70  bool hasEmittedDirection = false) {
71  auto emitTypeWithArguments =
72  [&]( // This is provided if the type is a base type, otherwise this is
73  // null
74  FIRRTLBaseType baseType, StringRef name,
75  // A lambda of type (bool hasEmittedDirection) -> LogicalResult.
76  auto emitArguments,
77  // Indicates whether parentheses around type arguments should be
78  // used.
79  bool emitParentheses = true) -> LogicalResult {
80  // Include the direction if the type is not a base (i.e. hardware) type or
81  // is not composed of flips and analog signals and we haven't already
82  // emitted the direction before recursing to this field.
83  // Chisel direction functions override any internal directions. In other
84  // words, Output(new Bundle {...}) erases all direction information inside
85  // the bundle. Because of this, directions are placed on the outermost
86  // passive members of a hardware type.
87  bool emitDirection =
88  !baseType || (!hasEmittedDirection && baseType.isPassive() &&
89  !baseType.containsAnalog());
90  if (emitDirection) {
91  switch (direction) {
92  case Direction::In:
93  os << "Input(";
94  break;
95  case Direction::Out:
96  os << "Output(";
97  break;
98  }
99  }
100 
101  bool emitConst = baseType && baseType.isConst();
102  if (emitConst)
103  os << "Const(";
104 
105  os << name;
106 
107  if (emitParentheses)
108  os << "(";
109 
110  if (failed(emitArguments(hasEmittedDirection || emitDirection)))
111  return failure();
112 
113  if (emitParentheses)
114  os << ')';
115 
116  if (emitConst)
117  os << ')';
118 
119  if (emitDirection)
120  os << ')';
121 
122  return success();
123  };
124 
125  // Emits a type that does not require arguments.
126  auto emitType = [&](FIRRTLBaseType baseType,
127  StringRef name) -> LogicalResult {
128  return emitTypeWithArguments(baseType, name,
129  [](bool) { return success(); });
130  };
131 
132  // Emits a type that requires a known width argument.
133  auto emitWidthQualifiedType = [&](auto type,
134  StringRef name) -> LogicalResult {
135  auto width = type.getWidth();
136  if (!width.has_value()) {
137  return LogicalResult(emitError(
138  location, "Expected width to be inferred for exported port"));
139  }
140  return emitTypeWithArguments(type, name, [&](bool) {
141  os << *width << ".W";
142  return success();
143  });
144  };
145 
146  return TypeSwitch<Type, LogicalResult>(type)
147  .Case<ClockType>(
148  [&](ClockType type) { return emitType(type, "Clock"); })
149  .Case<AsyncResetType>(
150  [&](AsyncResetType type) { return emitType(type, "AsyncReset"); })
151  .Case<ResetType>([&](ResetType) {
152  return emitError(
153  location, "Expected reset type to be inferred for exported port");
154  })
155  .Case<UIntType>([&](UIntType uIntType) {
156  return emitWidthQualifiedType(uIntType, "UInt");
157  })
158  .Case<SIntType>([&](SIntType sIntType) {
159  return emitWidthQualifiedType(sIntType, "SInt");
160  })
161  .Case<AnalogType>([&](AnalogType analogType) {
162  return emitWidthQualifiedType(analogType, "Analog");
163  })
164  .Case<BundleType>([&](BundleType bundleType) {
165  // Emit an anonymous bundle, emitting a `val` for each field.
166  return emitTypeWithArguments(
167  bundleType, "new Bundle ",
168  [&](bool hasEmittedDirection) {
169  os << "{\n";
170  unsigned int nestedIndent = indent + indentIncrement;
171  for (const auto &element : bundleType.getElements()) {
172  os.indent(nestedIndent)
173  << "val " << element.name.getValue() << " = ";
174  auto elementResult = emitPortType(
175  location, element.type,
176  element.isFlip ? direction::flip(direction) : direction,
177  nestedIndent, hasEmittedDirection);
178  if (failed(elementResult))
179  return failure();
180  os << '\n';
181  }
182  os.indent(indent) << "}";
183  return success();
184  },
185  false);
186  })
187  .Case<FVectorType>([&](FVectorType vectorType) {
188  // Emit a vector type, emitting the type of its element as an
189  // argument.
190  return emitTypeWithArguments(
191  vectorType, "Vec", [&](bool hasEmittedDirection) {
192  os << vectorType.getNumElements() << ", ";
193  return emitPortType(location, vectorType.getElementType(),
194  direction, indent, hasEmittedDirection);
195  });
196  })
197  .Case<RefType>([&](RefType refType) {
198  hasEmittedProbe = true;
199  StringRef name = refType.getForceable() ? "RWProbe" : "Probe";
200  return emitTypeWithArguments(
201  nullptr, name, [&](bool hasEmittedDirection) {
202  return emitPortType(location, refType.getType(), direction,
203  indent, hasEmittedDirection);
204  });
205  })
206  .Default([&](Type type) {
207  mlir::emitError(location) << "Unhandled type: " << type;
208  return failure();
209  });
210  }
211 
212  llvm::raw_ostream &os;
213  bool hasEmittedProbe = false;
214 };
215 } // namespace
216 
217 /// Exports a Chisel interface to the output stream.
218 static LogicalResult exportChiselInterface(CircuitOp circuit,
219  llvm::raw_ostream &os) {
220  // Emit version, package, and import declarations
221  os << circt::getCirctVersionComment() << "package shelf."
222  << circuit.getName().lower()
223  << "\n\nimport chisel3._\nimport chisel3.experimental._\n";
224 
225  std::string body;
226  llvm::raw_string_ostream bodyStream(body);
227  Emitter emitter(bodyStream);
228 
229  // Emit a class for the main circuit module.
230  for (auto moduleOp : circuit.getOps<FModuleOp>()) {
231  if (!moduleOp.isPublic())
232  continue;
233  if (failed(emitter.emitModule(moduleOp)))
234  return failure();
235  }
236 
237  // Emit an import for probe types if needed
238  if (emitter.hasEmittedProbeType())
239  os << "import chisel3.probe._\n";
240 
241  // Emit the body
242  os << '\n' << body;
243 
244  return success();
245 }
246 
247 /// Exports Chisel interface files for the circuit to the specified directory.
248 static LogicalResult exportSplitChiselInterface(CircuitOp circuit,
249  StringRef outputDirectory) {
250  // Create the output directory if needed.
251  std::error_code error = llvm::sys::fs::create_directories(outputDirectory);
252  if (error) {
253  circuit.emitError("cannot create output directory \"")
254  << outputDirectory << "\": " << error.message();
255  return failure();
256  }
257 
258  // Open the output file.
259  SmallString<128> interfaceFilePath(outputDirectory);
260  llvm::sys::path::append(interfaceFilePath, circuit.getName());
261  llvm::sys::path::replace_extension(interfaceFilePath, "scala");
262  std::string errorMessage;
263  auto interfaceFile = mlir::openOutputFile(interfaceFilePath, &errorMessage);
264  if (!interfaceFile) {
265  circuit.emitError(errorMessage);
266  return failure();
267  }
268 
269  // Export the interface to the file.
270  auto result = exportChiselInterface(circuit, interfaceFile->os());
271  if (succeeded(result))
272  interfaceFile->keep();
273  return result;
274 }
275 
276 //===----------------------------------------------------------------------===//
277 // ExportChiselInterfacePass and ExportSplitChiselInterfacePass
278 //===----------------------------------------------------------------------===//
279 
280 namespace {
281 struct ExportChiselInterfacePass
282  : public ExportChiselInterfaceBase<ExportChiselInterfacePass> {
283 
284  explicit ExportChiselInterfacePass(llvm::raw_ostream &os) : os(os) {}
285 
286  void runOnOperation() override {
287  if (failed(exportChiselInterface(getOperation(), os)))
288  signalPassFailure();
289  }
290 
291 private:
292  llvm::raw_ostream &os;
293 };
294 
295 struct ExportSplitChiselInterfacePass
296  : public ExportSplitChiselInterfaceBase<ExportSplitChiselInterfacePass> {
297 
298  explicit ExportSplitChiselInterfacePass(StringRef directory) {
299  directoryName = directory.str();
300  }
301 
302  void runOnOperation() override {
303  if (failed(exportSplitChiselInterface(getOperation(), directoryName)))
304  signalPassFailure();
305  }
306 };
307 } // namespace
308 
309 std::unique_ptr<mlir::Pass>
311  return std::make_unique<ExportChiselInterfacePass>(os);
312 }
313 
314 std::unique_ptr<mlir::Pass>
316  return std::make_unique<ExportSplitChiselInterfacePass>(directory);
317 }
318 
319 std::unique_ptr<mlir::Pass> circt::createExportChiselInterfacePass() {
321 }
static LogicalResult exportSplitChiselInterface(CircuitOp circuit, StringRef outputDirectory)
Exports Chisel interface files for the circuit to the specified directory.
static const unsigned int indentIncrement
static LogicalResult exportChiselInterface(CircuitOp circuit, llvm::raw_ostream &os)
Exports a Chisel interface to the output stream.
int32_t width
Definition: FIRRTL.cpp:36
static StringAttr append(StringAttr base, const Twine &suffix)
Return a attribute with the specified suffix appended.
bool isConst()
Returns true if this is a 'const' type that can only hold compile-time constant values.
bool isPassive() const
Return true if this is a "passive" type - one that contains no "flip" types recursively within itself...
Definition: FIRRTLTypes.h:152
Direction
This represents the direction of a single port.
This file defines an intermediate representation for circuits acting as an abstraction for constraint...
Definition: DebugAnalysis.h:21
std::unique_ptr< mlir::Pass > createExportSplitChiselInterfacePass(mlir::StringRef outputDirectory="./")
std::unique_ptr< mlir::Pass > createExportChiselInterfacePass(llvm::raw_ostream &os)
const char * getCirctVersionComment()
mlir::raw_indented_ostream & outs()
Definition: Utility.h:38
This holds the name and type that describes the module's ports.
StringRef getName() const