Skip to content

Commit 316ee41

Browse files
authored
[firtool] Add an option to emit HW MLIR into file (#8169)
This commit adds -output-hw-mlir option to firtool which emits HW IR into file in a similar way to -output-final-mlir. This commit adds DumpIR pass to simplify the emission. It's currently not exposed to other tool as it's very specific to firtool pipeline (actually the pass indirectly uses cl options defined in firtool.cpp)
1 parent 2d2bee8 commit 316ee41

File tree

2 files changed

+66
-22
lines changed

2 files changed

+66
-22
lines changed

test/firtool/firtool.mlir

Lines changed: 16 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,24 +2,29 @@
22
// RUN: firtool %s --format=mlir --ir-fir --emit-bytecode | circt-opt | FileCheck %s --check-prefix=MLIR
33
// RUN: circt-opt %s --emit-bytecode | firtool --ir-fir | circt-opt | FileCheck %s --check-prefix=MLIR
44
// RUN: firtool %s --format=mlir -verilog | FileCheck %s --check-prefix=VERILOG
5-
// RUN: firtool %s --format=mlir -verilog -output-final-mlir=%t | FileCheck %s --check-prefix=VERILOG-WITH-MLIR
6-
// RUN: firtool %s --format=mlir -verilog -output-final-mlir=%t.mlirbc -emit-bytecode | FileCheck %s --check-prefix=VERILOG-WITH-MLIR
5+
// RUN: firtool %s --format=mlir -verilog -output-final-mlir=%t -output-hw-mlir=%t.hw.mlir | FileCheck %s --check-prefix=VERILOG-WITH-MLIR
6+
// RUN: firtool %s --format=mlir -verilog -output-final-mlir=%t.mlirbc -output-hw-mlir=%t.hw.mlirbc -emit-bytecode | FileCheck %s --check-prefix=VERILOG-WITH-MLIR
77
// RUN: FileCheck %s --input-file=%t --check-prefix=VERILOG-WITH-MLIR-OUT
88
// RUN: circt-opt %t.mlirbc | FileCheck %s --check-prefix=VERILOG-WITH-MLIR-OUT
99
// RUN: not diff %t %t.mlirbc
10+
// RUN: FileCheck %s --input-file=%t.hw.mlir --check-prefix=VERILOG-WITH-HW-MLIR-OUT
11+
// RUN: circt-opt %t.hw.mlirbc | FileCheck %s --check-prefix=VERILOG-WITH-HW-MLIR-OUT
12+
// RUN: not diff %t.hw.mlir %t.hw.mlirbc
13+
1014

1115
firrtl.circuit "Top" {
12-
firrtl.module @Top(in %in : !firrtl.uint<8>,
16+
firrtl.module @Top(in %clock: !firrtl.clock, in %in : !firrtl.uint<8>,
1317
out %out : !firrtl.uint<8>) {
1418
firrtl.connect %out, %in : !firrtl.uint<8>, !firrtl.uint<8>
1519
}
1620
}
1721

18-
// MLIR-LABEL: firrtl.module @Top(in %in: !firrtl.uint<8>, out %out: !firrtl.uint<8>) {
22+
// MLIR-LABEL: firrtl.module @Top(in %clock: !firrtl.clock, in %in: !firrtl.uint<8>, out %out: !firrtl.uint<8>) {
1923
// MLIR-NEXT: firrtl.matchingconnect %out, %in : !firrtl.uint<8>
2024
// MLIR-NEXT: }
2125

2226
// VERILOG-LABEL: module Top(
27+
// VERILOG-NEXT: input clock,
2328
// VERILOG-NEXT: input [7:0] in,
2429
// VERILOG-NEXT: output [7:0] out
2530
// VERILOG-NEXT: );
@@ -28,13 +33,19 @@ firrtl.circuit "Top" {
2833
// VERILOG-NEXT: endmodule
2934

3035
// VERILOG-WITH-MLIR-LABEL: module Top(
36+
// VERILOG-WITH-MLIR-NEXT: input clock,
3137
// VERILOG-WITH-MLIR-NEXT: input [7:0] in,
3238
// VERILOG-WITH-MLIR-NEXT: output [7:0] out
3339
// VERILOG-WITH-MLIR-NEXT: );
3440
// VERILOG-WITH-MLIR-EMPTY:
3541
// VERILOG-WITH-MLIR-NEXT: assign out = in;
3642
// VERILOG-WITH-MLIR-NEXT: endmodule
3743

38-
// VERILOG-WITH-MLIR-OUT-LABEL: hw.module @Top(in %in : i8, out out : i8) {
44+
// VERILOG-WITH-MLIR-OUT-LABEL: hw.module @Top(in %clock : i1, in %in : i8, out out : i8) {
3945
// VERILOG-WITH-MLIR-OUT-NEXT: hw.output %in : i8
4046
// VERILOG-WITH-MLIR-OUT-NEXT: }
47+
48+
// Check there is !seq.clock
49+
// VERILOG-WITH-HW-MLIR-OUT-LABEL: hw.module @Top(in %clock : !seq.clock, in %in : i8, out out : i8) {
50+
// VERILOG-WITH-HW-MLIR-OUT-NEXT: hw.output %in : i8
51+
// VERILOG-WITH-HW-MLIR-OUT-NEXT: }

tools/firtool/firtool.cpp

Lines changed: 50 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -206,6 +206,12 @@ static cl::list<std::string> inputAnnotationFilenames(
206206
"annotation-file", cl::desc("Optional input annotation file"),
207207
cl::CommaSeparated, cl::value_desc("filename"), cl::cat(mainCategory));
208208

209+
static cl::opt<std::string>
210+
hwOutFile("output-hw-mlir",
211+
cl::desc("Optional file name to output the HW IR into, in "
212+
"addition to the output requested by -o"),
213+
cl::init(""), cl::value_desc("filename"), cl::cat(mainCategory));
214+
209215
static cl::opt<std::string>
210216
mlirOutFile("output-final-mlir",
211217
cl::desc("Optional file name to output the final MLIR into, in "
@@ -336,6 +342,38 @@ struct EmitSplitHGLDDPass
336342
}
337343
};
338344

345+
/// Wrapper pass to dump IR.
346+
struct DumpIRPass
347+
: public PassWrapper<DumpIRPass, OperationPass<mlir::ModuleOp>> {
348+
DumpIRPass(const std::string &outputFile)
349+
: PassWrapper<DumpIRPass, OperationPass<mlir::ModuleOp>>() {
350+
this->outputFile.setValue(outputFile);
351+
}
352+
353+
DumpIRPass(const DumpIRPass &other) : PassWrapper(other) {
354+
outputFile.setValue(other.outputFile.getValue());
355+
}
356+
357+
void runOnOperation() override {
358+
assert(!outputFile.empty());
359+
360+
std::string error;
361+
auto mlirFile = openOutputFile(outputFile.getValue(), &error);
362+
if (!mlirFile) {
363+
errs() << error;
364+
return signalPassFailure();
365+
}
366+
367+
if (failed(printOp(getOperation(), mlirFile->os())))
368+
return signalPassFailure();
369+
mlirFile->keep();
370+
markAllAnalysesPreserved();
371+
}
372+
373+
Pass::Option<std::string> outputFile{*this, "output-file",
374+
cl::desc("filename"), cl::init("-")};
375+
};
376+
339377
/// Process a single buffer of the input.
340378
static LogicalResult processBuffer(
341379
MLIRContext &context, firtool::FirtoolOptions &firtoolOptions,
@@ -456,6 +494,11 @@ static LogicalResult processBuffer(
456494
if (failed(firtool::populateHWToBTOR2(pm, firtoolOptions,
457495
(*outputFile)->os())))
458496
return failure();
497+
498+
// If requested, emit the HW IR to hwOutFile.
499+
if (!hwOutFile.empty())
500+
pm.addPass(std::make_unique<DumpIRPass>(hwOutFile.getValue()));
501+
459502
if (outputFormat != OutputIRHW)
460503
if (failed(firtool::populateHWToSV(pm, firtoolOptions)))
461504
return failure();
@@ -504,11 +547,15 @@ static LogicalResult processBuffer(
504547
break;
505548
}
506549

507-
// Run final IR mutations to clean it up after ExportVerilog and before
508-
// emitting the final MLIR.
509-
if (!mlirOutFile.empty())
550+
// If requested, print the final MLIR into mlirOutFile.
551+
if (!mlirOutFile.empty()) {
552+
// Run final IR mutations to clean it up after ExportVerilog and before
553+
// emitting the final MLIR.
510554
if (failed(firtool::populateFinalizeIR(pm, firtoolOptions)))
511555
return failure();
556+
557+
pm.addPass(std::make_unique<DumpIRPass>(mlirOutFile.getValue()));
558+
}
512559
}
513560

514561
if (failed(pm.run(module.get())))
@@ -521,20 +568,6 @@ static LogicalResult processBuffer(
521568
return failure();
522569
}
523570

524-
// If requested, print the final MLIR into mlirOutFile.
525-
if (!mlirOutFile.empty()) {
526-
std::string mlirOutError;
527-
auto mlirFile = openOutputFile(mlirOutFile, &mlirOutError);
528-
if (!mlirFile) {
529-
llvm::errs() << mlirOutError;
530-
return failure();
531-
}
532-
533-
if (failed(printOp(*module, mlirFile->os())))
534-
return failure();
535-
mlirFile->keep();
536-
}
537-
538571
// We intentionally "leak" the Module into the MLIRContext instead of
539572
// deallocating it. There is no need to deallocate it right before process
540573
// exit.

0 commit comments

Comments
 (0)