diff --git a/include/circt/Dialect/SV/SVPasses.h b/include/circt/Dialect/SV/SVPasses.h index 8d12e64c216d..5884136e166c 100644 --- a/include/circt/Dialect/SV/SVPasses.h +++ b/include/circt/Dialect/SV/SVPasses.h @@ -40,7 +40,7 @@ createSVExtractTestCodePass(bool disableInstanceExtraction = false, bool disableRegisterExtraction = false, bool disableModuleInlining = false); std::unique_ptr -createHWExportModuleHierarchyPass(std::optional directory = {}); +createHWExportModuleHierarchyPass(); /// Generate the code for registering passes. #define GEN_PASS_REGISTRATION #include "circt/Dialect/SV/SVPasses.h.inc" diff --git a/include/circt/Firtool/Firtool.h b/include/circt/Firtool/Firtool.h index 2e18fc50e00d..658dfbba1c27 100644 --- a/include/circt/Firtool/Firtool.h +++ b/include/circt/Firtool/Firtool.h @@ -24,6 +24,23 @@ namespace firtool { struct FirtoolOptions { llvm::cl::OptionCategory &category; + llvm::cl::opt disableAnnotationsUnknown{ + "disable-annotation-unknown", + llvm::cl::desc("Ignore unknown annotations when parsing"), + llvm::cl::init(false), llvm::cl::cat(category)}; + + llvm::cl::opt disableAnnotationsClassless{ + "disable-annotation-classless", + llvm::cl::desc("Ignore annotations without a class when parsing"), + llvm::cl::init(false), llvm::cl::cat(category)}; + + llvm::cl::opt lowerAnnotationsNoRefTypePorts{ + "lower-annotations-no-ref-type-ports", + llvm::cl::desc( + "Create real ports instead of ref type ports when resolving " + "wiring problems inside the LowerAnnotations pass"), + llvm::cl::init(false), llvm::cl::Hidden, llvm::cl::cat(category)}; + llvm::cl::opt preserveAggregate{ "preserve-aggregate", llvm::cl::desc("Specify input file format:"), @@ -241,6 +258,22 @@ struct FirtoolOptions { llvm::cl::location(clockGateOpts.testEnableName), llvm::cl::init("test_en"), llvm::cl::cat(category)}; + llvm::cl::opt exportModuleHierarchy{ + "export-module-hierarchy", + llvm::cl::desc("Export module and instance hierarchy as JSON"), + llvm::cl::init(false), llvm::cl::cat(category)}; + + llvm::cl::opt stripFirDebugInfo{ + "strip-fir-debug-info", + llvm::cl::desc( + "Disable source fir locator information in output Verilog"), + llvm::cl::init(true), llvm::cl::cat(category)}; + + llvm::cl::opt stripDebugInfo{ + "strip-debug-info", + llvm::cl::desc("Disable source locator information in output Verilog"), + llvm::cl::init(false), llvm::cl::cat(category)}; + bool isRandomEnabled(RandomKind kind) const { return disableRandom != RandomKind::All && disableRandom != kind; } @@ -260,6 +293,9 @@ struct FirtoolOptions { FirtoolOptions(llvm::cl::OptionCategory &category) : category(category) {} }; +LogicalResult populateLowerAnnotations(mlir::PassManager &pm, + const FirtoolOptions &opt); + LogicalResult populateCHIRRTLToLowFIRRTL(mlir::PassManager &pm, const FirtoolOptions &opt, ModuleOp module, @@ -270,6 +306,20 @@ LogicalResult populateLowFIRRTLToHW(mlir::PassManager &pm, LogicalResult populateHWToSV(mlir::PassManager &pm, const FirtoolOptions &opt); +LogicalResult populatePrepareForExportVerilog(mlir::PassManager &pm, + const FirtoolOptions &opt); + +LogicalResult populateExportVerilog(mlir::PassManager &pm, + const FirtoolOptions &opt, + llvm::raw_ostream &os); + +LogicalResult populateExportSplitVerilog(mlir::PassManager &pm, + const FirtoolOptions &opt, + llvm::StringRef directory); + +LogicalResult populateFinalizeIR(mlir::PassManager &pm, + const FirtoolOptions &opt); + } // namespace firtool } // namespace circt diff --git a/lib/Dialect/SV/Transforms/HWExportModuleHierarchy.cpp b/lib/Dialect/SV/Transforms/HWExportModuleHierarchy.cpp index 63230349093f..361c3cdb3ee4 100644 --- a/lib/Dialect/SV/Transforms/HWExportModuleHierarchy.cpp +++ b/lib/Dialect/SV/Transforms/HWExportModuleHierarchy.cpp @@ -138,6 +138,6 @@ void HWExportModuleHierarchyPass::runOnOperation() { //===----------------------------------------------------------------------===// std::unique_ptr -sv::createHWExportModuleHierarchyPass(std::optional directory) { +sv::createHWExportModuleHierarchyPass() { return std::make_unique(); } diff --git a/lib/Firtool/CMakeLists.txt b/lib/Firtool/CMakeLists.txt index e2677820d77f..6a6f99b2d61a 100644 --- a/lib/Firtool/CMakeLists.txt +++ b/lib/Firtool/CMakeLists.txt @@ -3,6 +3,7 @@ add_circt_library(CIRCTFirtool LINK_LIBS PUBLIC CIRCTExportChiselInterface + CIRCTExportVerilog CIRCTFIRRTLToHW CIRCTFIRRTLTransforms CIRCTHWTransforms diff --git a/lib/Firtool/Firtool.cpp b/lib/Firtool/Firtool.cpp index fc5d437e1898..88ed41c5c2e4 100644 --- a/lib/Firtool/Firtool.cpp +++ b/lib/Firtool/Firtool.cpp @@ -14,6 +14,7 @@ #include "circt/Dialect/SV/SVPasses.h" #include "circt/Dialect/Seq/SeqPasses.h" #include "circt/Support/Passes.h" +#include "circt/Transforms/Passes.h" #include "mlir/Transforms/Passes.h" #include "llvm/Support/FileSystem.h" #include "llvm/Support/Path.h" @@ -21,6 +22,20 @@ using namespace llvm; using namespace circt; +LogicalResult firtool::populateLowerAnnotations(mlir::PassManager &pm, + const FirtoolOptions &opt) { + // Legalize away "open" aggregates to hw-only versions. + pm.nest().addPass(firrtl::createLowerOpenAggsPass()); + + pm.nest().addPass(firrtl::createResolvePathsPass()); + + pm.nest().addPass(firrtl::createLowerFIRRTLAnnotationsPass( + opt.disableAnnotationsUnknown, opt.disableAnnotationsClassless, + opt.lowerAnnotationsNoRefTypePorts)); + + return success(); +} + LogicalResult firtool::populateCHIRRTLToLowFIRRTL(mlir::PassManager &pm, const FirtoolOptions &opt, ModuleOp module, @@ -243,3 +258,58 @@ LogicalResult firtool::populateHWToSV(mlir::PassManager &pm, return success(); } + +LogicalResult +firtool::populatePrepareForExportVerilog(mlir::PassManager &pm, + const FirtoolOptions &opt) { + + // Legalize unsupported operations within the modules. + pm.nest().addPass(sv::createHWLegalizeModulesPass()); + + // Tidy up the IR to improve verilog emission quality. + if (!opt.disableOptimization) + pm.nest().addPass(sv::createPrettifyVerilogPass()); + + if (opt.stripFirDebugInfo) + pm.addPass(circt::createStripDebugInfoWithPredPass([](mlir::Location loc) { + if (auto fileLoc = loc.dyn_cast()) + return fileLoc.getFilename().getValue().endswith(".fir"); + return false; + })); + + if (opt.stripDebugInfo) + pm.addPass(circt::createStripDebugInfoWithPredPass( + [](mlir::Location loc) { return true; })); + + // Emit module and testbench hierarchy JSON files. + if (opt.exportModuleHierarchy) + pm.addPass(sv::createHWExportModuleHierarchyPass()); + + // Check inner symbols and inner refs. + pm.addPass(hw::createVerifyInnerRefNamespacePass()); + + return success(); +} + +LogicalResult firtool::populateExportVerilog(mlir::PassManager &pm, + const FirtoolOptions &opt, + llvm::raw_ostream &os) { + pm.addPass(createExportVerilogPass(os)); + + return success(); +} + +LogicalResult firtool::populateExportSplitVerilog(mlir::PassManager &pm, + const FirtoolOptions &opt, + llvm::StringRef directory) { + pm.addPass(createExportSplitVerilogPass(directory)); + + return success(); +} + +LogicalResult firtool::populateFinalizeIR(mlir::PassManager &pm, + const FirtoolOptions &opt) { + pm.addPass(firrtl::createFinalizeIRPass()); + + return success(); +} diff --git a/tools/firtool/firtool.cpp b/tools/firtool/firtool.cpp index bffca3aaaa61..287364b130fd 100644 --- a/tools/firtool/firtool.cpp +++ b/tools/firtool/firtool.cpp @@ -34,7 +34,6 @@ #include "circt/Support/LoweringOptionsParser.h" #include "circt/Support/Passes.h" #include "circt/Support/Version.h" -#include "circt/Transforms/Passes.h" #include "mlir/Bytecode/BytecodeReader.h" #include "mlir/Bytecode/BytecodeWriter.h" #include "mlir/Dialect/Func/IR/FuncOps.h" @@ -106,22 +105,6 @@ static cl::opt "expected-* lines on the corresponding line"), cl::init(false), cl::Hidden, cl::cat(mainCategory)); -static cl::opt disableAnnotationsClassless( - "disable-annotation-classless", - cl::desc("Ignore annotations without a class when parsing"), - cl::init(false), cl::cat(mainCategory)); - -static cl::opt disableAnnotationsUnknown( - "disable-annotation-unknown", - cl::desc("Ignore unknown annotations when parsing"), cl::init(false), - cl::cat(mainCategory)); - -static cl::opt lowerAnnotationsNoRefTypePorts( - "lower-annotations-no-ref-type-ports", - cl::desc("Create real ports instead of ref type ports when resolving " - "wiring problems inside the LowerAnnotations pass"), - cl::init(false), cl::Hidden, cl::cat(mainCategory)); - using InfoLocHandling = firrtl::FIRParserOptions::InfoLocHandling; static cl::opt infoLocHandling( cl::desc("Location tracking:"), @@ -135,11 +118,6 @@ static cl::opt infoLocHandling( "Use @info locations when present, fallback to .fir locations")), cl::init(InfoLocHandling::PreferInfo), cl::cat(mainCategory)); -static cl::opt exportModuleHierarchy( - "export-module-hierarchy", - cl::desc("Export module and instance hierarchy as JSON"), cl::init(false), - cl::cat(mainCategory)); - static cl::opt scalarizeTopModule("scalarize-top-module", cl::desc("Scalarize the ports of the top module"), @@ -214,16 +192,6 @@ static cl::opt cl::desc("Log executions of toplevel module passes"), cl::init(false), cl::cat(mainCategory)); -static cl::opt stripFirDebugInfo( - "strip-fir-debug-info", - cl::desc("Disable source fir locator information in output Verilog"), - cl::init(true), cl::cat(mainCategory)); - -static cl::opt stripDebugInfo( - "strip-debug-info", - cl::desc("Disable source locator information in output Verilog"), - cl::init(false), cl::cat(mainCategory)); - static LoweringOptionsOption loweringOptions(mainCategory); /// Check output stream before writing bytecode to it. @@ -323,14 +291,8 @@ static LogicalResult processBuffer( if (failed(applyPassManagerCLOptions(pm))) return failure(); - // Legalize away "open" aggregates to hw-only versions. - pm.nest().addPass(firrtl::createLowerOpenAggsPass()); - - pm.nest().addPass(firrtl::createResolvePathsPass()); - - pm.nest().addPass(firrtl::createLowerFIRRTLAnnotationsPass( - disableAnnotationsUnknown, disableAnnotationsClassless, - lowerAnnotationsNoRefTypePorts)); + if (failed(firtool::populateLowerAnnotations(pm, firtoolOptions))) + return failure(); // If the user asked for --parse-only, stop after running LowerAnnotations. if (outputFormat == OutputParseOnly) { @@ -361,52 +323,36 @@ static LogicalResult processBuffer( // Add passes specific to Verilog emission if we're going there. if (outputFormat == OutputVerilog || outputFormat == OutputSplitVerilog || outputFormat == OutputIRVerilog) { - // Legalize unsupported operations within the modules. - pm.nest().addPass(sv::createHWLegalizeModulesPass()); - - // Tidy up the IR to improve verilog emission quality. - if (!firtoolOptions.disableOptimization) - pm.nest().addPass(sv::createPrettifyVerilogPass()); - - if (stripFirDebugInfo) - pm.addPass( - circt::createStripDebugInfoWithPredPass([](mlir::Location loc) { - if (auto fileLoc = loc.dyn_cast()) - return fileLoc.getFilename().getValue().endswith(".fir"); - return false; - })); - - if (stripDebugInfo) - pm.addPass(circt::createStripDebugInfoWithPredPass( - [](mlir::Location loc) { return true; })); - - // Emit module and testbench hierarchy JSON files. - if (exportModuleHierarchy) - pm.addPass(sv::createHWExportModuleHierarchyPass(outputFilename)); - - // Check inner symbols and inner refs. - pm.addPass(hw::createVerifyInnerRefNamespacePass()); + if (failed(firtool::populatePrepareForExportVerilog(pm, firtoolOptions))) + return failure(); // Emit a single file or multiple files depending on the output format. switch (outputFormat) { default: llvm_unreachable("can't reach this"); case OutputVerilog: - pm.addPass(createExportVerilogPass((*outputFile)->os())); + if (failed(firtool::populateExportVerilog(pm, firtoolOptions, + (*outputFile)->os()))) + return failure(); break; case OutputSplitVerilog: - pm.addPass(createExportSplitVerilogPass(outputFilename)); + if (failed(firtool::populateExportSplitVerilog(pm, firtoolOptions, + outputFilename))) + return failure(); break; case OutputIRVerilog: // Run the ExportVerilog pass to get its lowering, but discard the output. - pm.addPass(createExportVerilogPass(llvm::nulls())); + if (failed(firtool::populateExportVerilog(pm, firtoolOptions, + llvm::nulls()))) + return failure(); break; } // Run final IR mutations to clean it up after ExportVerilog and before // emitting the final MLIR. if (!mlirOutFile.empty()) - pm.addPass(firrtl::createFinalizeIRPass()); + if (failed(firtool::populateFinalizeIR(pm, firtoolOptions))) + return failure(); } if (failed(pm.run(module.get())))