14 #include "../PassDetail.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"
22 using namespace circt;
23 using namespace firrtl;
25 #define DEBUG_TYPE "export-chisel-package"
36 Emitter(llvm::raw_ostream &os) : os(os) {}
38 bool hasEmittedProbeType() {
return hasEmittedProbe; }
41 LogicalResult emitModule(FModuleLike module) {
42 os <<
"class " << module.getModuleName() <<
" extends ExtModule {\n";
44 for (
const auto &port : module.getPorts()) {
45 if (failed(emitPort(port)))
56 LogicalResult emitPort(
const PortInfo &port) {
68 LogicalResult emitPortType(Location location, Type type,
Direction direction,
70 bool hasEmittedDirection =
false) {
71 auto emitTypeWithArguments =
79 bool emitParentheses =
true) -> LogicalResult {
88 !baseType || (!hasEmittedDirection && baseType.
isPassive() &&
89 !baseType.containsAnalog());
101 bool emitConst = baseType && baseType.
isConst();
110 if (failed(emitArguments(hasEmittedDirection || emitDirection)))
127 StringRef name) -> LogicalResult {
128 return emitTypeWithArguments(baseType, name,
129 [](
bool) {
return success(); });
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"));
140 return emitTypeWithArguments(type, name, [&](
bool) {
141 os << *
width <<
".W";
146 return TypeSwitch<Type, LogicalResult>(type)
148 [&](ClockType type) {
return emitType(type,
"Clock"); })
149 .Case<AsyncResetType>(
150 [&](AsyncResetType type) {
return emitType(type,
"AsyncReset"); })
151 .Case<ResetType>([&](ResetType) {
153 location,
"Expected reset type to be inferred for exported port");
155 .Case<UIntType>([&](UIntType uIntType) {
156 return emitWidthQualifiedType(uIntType,
"UInt");
158 .Case<SIntType>([&](SIntType sIntType) {
159 return emitWidthQualifiedType(sIntType,
"SInt");
161 .Case<AnalogType>([&](AnalogType analogType) {
162 return emitWidthQualifiedType(analogType,
"Analog");
164 .Case<BundleType>([&](BundleType bundleType) {
166 return emitTypeWithArguments(
167 bundleType,
"new Bundle ",
168 [&](
bool hasEmittedDirection) {
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))
182 os.indent(indent) <<
"}";
187 .Case<FVectorType>([&](FVectorType vectorType) {
190 return emitTypeWithArguments(
191 vectorType,
"Vec", [&](
bool hasEmittedDirection) {
192 os << vectorType.getNumElements() <<
", ";
193 return emitPortType(location, vectorType.getElementType(),
194 direction, indent, hasEmittedDirection);
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);
206 .Default([&](Type type) {
207 mlir::emitError(location) <<
"Unhandled type: " << type;
212 llvm::raw_ostream &os;
213 bool hasEmittedProbe =
false;
219 llvm::raw_ostream &os) {
222 << circuit.getName().lower()
223 <<
"\n\nimport chisel3._\nimport chisel3.experimental._\n";
226 llvm::raw_string_ostream bodyStream(body);
227 Emitter emitter(bodyStream);
230 auto topModule = circuit.getMainModule();
231 if (failed(emitter.emitModule(topModule)))
235 if (emitter.hasEmittedProbeType())
236 os <<
"import chisel3.probe._\n";
246 StringRef outputDirectory) {
248 std::error_code error = llvm::sys::fs::create_directories(outputDirectory);
250 circuit.emitError(
"cannot create output directory \"")
251 << outputDirectory <<
"\": " << error.message();
256 SmallString<128> interfaceFilePath(outputDirectory);
258 llvm::sys::path::replace_extension(interfaceFilePath,
"scala");
259 std::string errorMessage;
260 auto interfaceFile = mlir::openOutputFile(interfaceFilePath, &errorMessage);
261 if (!interfaceFile) {
262 circuit.emitError(errorMessage);
268 if (succeeded(result))
269 interfaceFile->keep();
278 struct ExportChiselInterfacePass
279 :
public ExportChiselInterfaceBase<ExportChiselInterfacePass> {
281 explicit ExportChiselInterfacePass(llvm::raw_ostream &os) : os(os) {}
283 void runOnOperation()
override {
289 llvm::raw_ostream &os;
292 struct ExportSplitChiselInterfacePass
293 :
public ExportSplitChiselInterfaceBase<ExportSplitChiselInterfacePass> {
295 explicit ExportSplitChiselInterfacePass(StringRef directory) {
296 directoryName = directory.str();
299 void runOnOperation()
override {
306 std::unique_ptr<mlir::Pass>
308 return std::make_unique<ExportChiselInterfacePass>(os);
311 std::unique_ptr<mlir::Pass>
313 return std::make_unique<ExportSplitChiselInterfacePass>(directory);
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.
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...
Direction
This represents the direction of a single port.
This file defines an intermediate representation for circuits acting as an abstraction for constraint...
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()
This holds the name and type that describes the module's ports.
StringRef getName() const