16 #include "mlir/Support/FileUtilities.h"
17 #include "llvm/Support/FileSystem.h"
18 #include "llvm/Support/Path.h"
19 #include "llvm/Support/ToolOutputFile.h"
22 #define GEN_PASS_DEF_EXPORTCHISELINTERFACE
23 #define GEN_PASS_DEF_EXPORTSPLITCHISELINTERFACE
24 #include "circt/Conversion/Passes.h.inc"
27 using namespace circt;
28 using namespace firrtl;
30 #define DEBUG_TYPE "export-chisel-package"
41 Emitter(llvm::raw_ostream &os) : os(os) {}
43 bool hasEmittedProbeType() {
return hasEmittedProbe; }
46 LogicalResult emitModule(FModuleLike module) {
47 os <<
"class " << module.getModuleName() <<
" extends ExtModule {\n";
49 for (
const auto &port : module.getPorts()) {
50 if (failed(emitPort(port)))
61 LogicalResult emitPort(
const PortInfo &port) {
73 LogicalResult emitPortType(Location location, Type type,
Direction direction,
75 bool hasEmittedDirection =
false) {
76 auto emitTypeWithArguments =
84 bool emitParentheses =
true) -> LogicalResult {
93 !baseType || (!hasEmittedDirection && baseType.
isPassive() &&
94 !baseType.containsAnalog());
106 bool emitConst = baseType && baseType.
isConst();
115 if (failed(emitArguments(hasEmittedDirection || emitDirection)))
132 StringRef name) -> LogicalResult {
133 return emitTypeWithArguments(baseType, name,
134 [](
bool) {
return success(); });
138 auto emitWidthQualifiedType = [&](
auto type,
139 StringRef name) -> LogicalResult {
140 auto width = type.getWidth();
141 if (!width.has_value()) {
142 return LogicalResult(emitError(
143 location,
"Expected width to be inferred for exported port"));
145 return emitTypeWithArguments(type, name, [&](
bool) {
146 os << *width <<
".W";
151 return TypeSwitch<Type, LogicalResult>(type)
153 [&](ClockType type) {
return emitType(type,
"Clock"); })
154 .Case<AsyncResetType>(
155 [&](AsyncResetType type) {
return emitType(type,
"AsyncReset"); })
156 .Case<ResetType>([&](ResetType) {
158 location,
"Expected reset type to be inferred for exported port");
160 .Case<UIntType>([&](UIntType uIntType) {
161 return emitWidthQualifiedType(uIntType,
"UInt");
163 .Case<SIntType>([&](SIntType sIntType) {
164 return emitWidthQualifiedType(sIntType,
"SInt");
166 .Case<AnalogType>([&](AnalogType analogType) {
167 return emitWidthQualifiedType(analogType,
"Analog");
169 .Case<BundleType>([&](BundleType bundleType) {
171 return emitTypeWithArguments(
172 bundleType,
"new Bundle ",
173 [&](
bool hasEmittedDirection) {
176 for (
const auto &element : bundleType.getElements()) {
177 os.indent(nestedIndent)
178 <<
"val " << element.name.getValue() <<
" = ";
179 auto elementResult = emitPortType(
180 location, element.type,
181 element.isFlip ? direction::flip(direction) : direction,
182 nestedIndent, hasEmittedDirection);
183 if (failed(elementResult))
187 os.indent(indent) <<
"}";
192 .Case<FVectorType>([&](FVectorType vectorType) {
195 return emitTypeWithArguments(
196 vectorType,
"Vec", [&](
bool hasEmittedDirection) {
197 os << vectorType.getNumElements() <<
", ";
198 return emitPortType(location, vectorType.getElementType(),
199 direction, indent, hasEmittedDirection);
202 .Case<RefType>([&](RefType refType) {
203 hasEmittedProbe =
true;
204 StringRef name = refType.getForceable() ?
"RWProbe" :
"Probe";
205 return emitTypeWithArguments(
206 nullptr, name, [&](
bool hasEmittedDirection) {
207 return emitPortType(location, refType.getType(), direction,
208 indent, hasEmittedDirection);
211 .Default([&](Type type) {
212 mlir::emitError(location) <<
"Unhandled type: " << type;
217 llvm::raw_ostream &os;
218 bool hasEmittedProbe =
false;
224 llvm::raw_ostream &os) {
227 << circuit.getName().lower()
228 <<
"\n\nimport chisel3._\nimport chisel3.experimental._\n";
231 llvm::raw_string_ostream bodyStream(body);
232 Emitter emitter(bodyStream);
235 for (
auto moduleOp : circuit.getOps<FModuleOp>()) {
236 if (!moduleOp.isPublic())
238 if (failed(emitter.emitModule(moduleOp)))
243 if (emitter.hasEmittedProbeType())
244 os <<
"import chisel3.probe._\n";
254 StringRef outputDirectory) {
256 std::error_code error = llvm::sys::fs::create_directories(outputDirectory);
258 circuit.emitError(
"cannot create output directory \"")
259 << outputDirectory <<
"\": " << error.message();
264 SmallString<128> interfaceFilePath(outputDirectory);
266 llvm::sys::path::replace_extension(interfaceFilePath,
"scala");
267 std::string errorMessage;
268 auto interfaceFile = mlir::openOutputFile(interfaceFilePath, &errorMessage);
269 if (!interfaceFile) {
270 circuit.emitError(errorMessage);
276 if (succeeded(result))
277 interfaceFile->keep();
286 struct ExportChiselInterfacePass
287 :
public circt::impl::ExportChiselInterfaceBase<ExportChiselInterfacePass> {
289 explicit ExportChiselInterfacePass(llvm::raw_ostream &os) : os(os) {}
291 void runOnOperation()
override {
297 llvm::raw_ostream &os;
300 struct ExportSplitChiselInterfacePass
301 :
public circt::impl::ExportSplitChiselInterfaceBase<
302 ExportSplitChiselInterfacePass> {
304 explicit ExportSplitChiselInterfacePass(StringRef directory) {
305 directoryName = directory.str();
308 void runOnOperation()
override {
315 std::unique_ptr<mlir::Pass>
317 return std::make_unique<ExportChiselInterfacePass>(os);
320 std::unique_ptr<mlir::Pass>
322 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.
The InstanceGraph op interface, see InstanceGraphInterface.td for more details.
std::unique_ptr< mlir::Pass > createExportSplitChiselInterfacePass(mlir::StringRef outputDirectory="./")
std::unique_ptr< mlir::Pass > createExportChiselInterfacePass(llvm::raw_ostream &os)
const char * getCirctVersionComment()
This holds the name and type that describes the module's ports.
StringRef getName() const