diff --git a/include/circt-c/Synthesis.h b/include/circt-c/Synthesis.h new file mode 100644 index 0000000000..69f748f00f --- /dev/null +++ b/include/circt-c/Synthesis.h @@ -0,0 +1,24 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef CIRCT_C_SYNTHESIS_H +#define CIRCT_C_SYNTHESIS_H + +#include "mlir-c/IR.h" + +#ifdef __cplusplus +extern "C" { +#endif + +MLIR_CAPI_EXPORTED void registerSynthesisPipeline(void); + +#ifdef __cplusplus +} +#endif + +#endif // CIRCT_C_SYNTHESIS_H diff --git a/include/circt/Synthesis/SynthesisPipeline.h b/include/circt/Synthesis/SynthesisPipeline.h new file mode 100644 index 0000000000..0149939ab3 --- /dev/null +++ b/include/circt/Synthesis/SynthesisPipeline.h @@ -0,0 +1,58 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This file defines the default synthesis pipeline. +// +//===----------------------------------------------------------------------===// + +#ifndef LIB_SYNTHESIS_SYNTHESISPIPELINE_H +#define LIB_SYNTHESIS_SYNTHESISPIPELINE_H + +#include "circt-c/Synthesis.h" +#include "mlir/Pass/PassManager.h" +#include "mlir/Pass/PassOptions.h" +#include +#include + +//===----------------------------------------------------------------------===// +// Pipeline Options +//===----------------------------------------------------------------------===// +namespace circt { +namespace synthesis { + +/// Options for the aig optimization pipeline. +struct AIGOptimizationPipelineOptions + : public mlir::PassPipelineOptions { + PassOptions::ListOption abcCommands{ + *this, "abc-commands", llvm::cl::desc("ABC passes to run")}; + + PassOptions::Option abcPath{ + *this, "abc-path", llvm::cl::desc("Path to ABC"), llvm::cl::init("abc")}; + + PassOptions::Option ignoreAbcFailures{ + *this, "ignore-abc-failures", + llvm::cl::desc("Continue on ABC failure instead of aborting"), + llvm::cl::init(false)}; +}; + +//===----------------------------------------------------------------------===// +// Pipeline Functions +//===----------------------------------------------------------------------===// + +/// Populate the synthesis pipelines. +void buildAIGLoweringPipeline(mlir::OpPassManager &pm); +void buildAIGOptimizationPipeline( + mlir::OpPassManager &pm, const AIGOptimizationPipelineOptions &options); + +/// Register the synthesis pipelines. +void registerSynthesisPipeline(); + +} // namespace synthesis +} // namespace circt + +#endif // LIB_SYNTHESIS_SYNTHESISPIPELINE_H diff --git a/integration_test/Bindings/Python/synthesis.py b/integration_test/Bindings/Python/synthesis.py new file mode 100644 index 0000000000..0e10f44b79 --- /dev/null +++ b/integration_test/Bindings/Python/synthesis.py @@ -0,0 +1,36 @@ +# REQUIRES: bindings_python +# RUN: %PYTHON% %s | FileCheck %s + +import circt +from circt.dialects import hw, comb +from circt.ir import Context, Location, Module, InsertionPoint, IntegerType +from circt.passmanager import PassManager + +with Context() as ctx, Location.unknown(): + circt.register_dialects(ctx) + + m = Module.create() + with InsertionPoint(m.body): + i4 = IntegerType.get_signless(4) + + # Create a module with comb.mul + def build_module(module): + a, b = module.entry_block.arguments + hw.OutputOp([comb.mul([a, b])]) + + hw.HWModuleOp( + name="foo", + input_ports=[("a", i4), ("b", i4)], + output_ports=[("out", i4)], + body_builder=build_module, + ) + + # Check that the synthesis pipeline is registered. + pm = PassManager.parse( + "builtin.module(hw.module(synthesis-aig-lowering-pipeline, " + "synthesis-aig-optimization-pipeline))") + pm.run(m.operation) + # CHECK: hw.module @foo( + # CHECK-NOT: comb.mul + # CHECK: aig.and_inv + print(m.operation) diff --git a/lib/Bindings/Python/CIRCTModule.cpp b/lib/Bindings/Python/CIRCTModule.cpp index e9320155fc..b077edc96a 100644 --- a/lib/Bindings/Python/CIRCTModule.cpp +++ b/lib/Bindings/Python/CIRCTModule.cpp @@ -26,6 +26,7 @@ #include "circt-c/Dialect/OM.h" #include "circt-c/Dialect/Pipeline.h" #include "circt-c/Dialect/RTG.h" +#include "circt-c/Synthesis.h" #include "circt-c/Transforms.h" #ifdef CIRCT_INCLUDE_TESTS #include "circt-c/Dialect/RTGTest.h" @@ -63,6 +64,7 @@ static void registerPasses() { registerHandshakePasses(); registerKanagawaPasses(); registerPipelinePasses(); + registerSynthesisPipeline(); mlirRegisterCIRCTConversionPasses(); mlirRegisterCIRCTTransformsPasses(); mlirRegisterTransformsCSE(); diff --git a/lib/Bindings/Python/CMakeLists.txt b/lib/Bindings/Python/CMakeLists.txt index 4c54feb2ea..471c8b7c8e 100644 --- a/lib/Bindings/Python/CMakeLists.txt +++ b/lib/Bindings/Python/CMakeLists.txt @@ -55,6 +55,7 @@ set(PYTHON_BINDINGS_LINK_LIBS CIRCTCAPISV CIRCTCAPIVerif CIRCTCAPITransforms + CIRCTCAPISynthesis MLIRCAPIIndex MLIRCAPISMT MLIRCAPIExportSMTLIB diff --git a/lib/CAPI/CMakeLists.txt b/lib/CAPI/CMakeLists.txt index 70753aa3a8..da90481e0f 100644 --- a/lib/CAPI/CMakeLists.txt +++ b/lib/CAPI/CMakeLists.txt @@ -4,4 +4,5 @@ add_subdirectory(ExportVerilog) add_subdirectory(Dialect) add_subdirectory(Firtool) add_subdirectory(RtgTool) +add_subdirectory(Synthesis) add_subdirectory(Transforms) diff --git a/lib/CAPI/Synthesis/CMakeLists.txt b/lib/CAPI/Synthesis/CMakeLists.txt new file mode 100644 index 0000000000..5e81070995 --- /dev/null +++ b/lib/CAPI/Synthesis/CMakeLists.txt @@ -0,0 +1,7 @@ +add_circt_public_c_api_library(CIRCTCAPISynthesis + SynthesisPipeline.cpp + + LINK_LIBS PUBLIC + CIRCTSynthesis + MLIRCAPIIR +) diff --git a/lib/CAPI/Synthesis/SynthesisPipeline.cpp b/lib/CAPI/Synthesis/SynthesisPipeline.cpp new file mode 100644 index 0000000000..6d27599033 --- /dev/null +++ b/lib/CAPI/Synthesis/SynthesisPipeline.cpp @@ -0,0 +1,15 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "circt-c/Synthesis.h" + +#include "circt/Synthesis/SynthesisPipeline.h" + +void registerSynthesisPipeline() { + circt::synthesis::registerSynthesisPipeline(); +} diff --git a/lib/CMakeLists.txt b/lib/CMakeLists.txt index 66b6e928ea..3e53d256c8 100644 --- a/lib/CMakeLists.txt +++ b/lib/CMakeLists.txt @@ -11,6 +11,7 @@ add_subdirectory(Firtool) add_subdirectory(Reduce) add_subdirectory(Scheduling) add_subdirectory(Support) +add_subdirectory(Synthesis) add_subdirectory(Target) add_subdirectory(Tools) add_subdirectory(Transforms) diff --git a/lib/Synthesis/CMakeLists.txt b/lib/Synthesis/CMakeLists.txt new file mode 100644 index 0000000000..25f1337bf3 --- /dev/null +++ b/lib/Synthesis/CMakeLists.txt @@ -0,0 +1,13 @@ +add_circt_library(CIRCTSynthesis + SynthesisPipeline.cpp + + LINK_LIBS PUBLIC + CIRCTAIGTransforms + CIRCTCombToAIG + CIRCTHWTransforms + CIRCTSupport + + MLIRIR + MLIRSupport + MLIRTransforms +) diff --git a/lib/Synthesis/SynthesisPipeline.cpp b/lib/Synthesis/SynthesisPipeline.cpp new file mode 100644 index 0000000000..d7a58c51fb --- /dev/null +++ b/lib/Synthesis/SynthesisPipeline.cpp @@ -0,0 +1,95 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This file implements the default synthesis pipeline from core dialect to AIG. +// +//===----------------------------------------------------------------------===// + +#include "circt/Synthesis/SynthesisPipeline.h" +#include "circt/Conversion/CombToAIG.h" +#include "circt/Dialect/AIG/AIGPasses.h" +#include "circt/Dialect/Comb/CombOps.h" +#include "circt/Dialect/HW/HWOps.h" +#include "circt/Dialect/HW/HWPasses.h" +#include "circt/Support/Passes.h" +#include "circt/Transforms/Passes.h" +#include "mlir/Pass/PassManager.h" +#include "mlir/Transforms/Passes.h" +#include "llvm/ADT/SmallVector.h" + +using namespace mlir; +using namespace circt; +using namespace circt::synthesis; + +//===----------------------------------------------------------------------===// +// Pipeline Implementation +//===----------------------------------------------------------------------===// + +/// Helper function to populate additional legal ops for partial legalization. +template +static void partiallyLegalizeCombToAIG(SmallVectorImpl &ops) { + (ops.push_back(AllowedOpTy::getOperationName().str()), ...); +} + +void circt::synthesis::buildAIGLoweringPipeline(OpPassManager &pm) { + { + // Partially legalize Comb to AIG, run CSE and canonicalization. + circt::ConvertCombToAIGOptions convOptions; + partiallyLegalizeCombToAIG( + convOptions.additionalLegalOps); + pm.addPass(circt::createConvertCombToAIG(convOptions)); + } + pm.addPass(createCSEPass()); + pm.addPass(createSimpleCanonicalizerPass()); + + pm.addPass(circt::hw::createHWAggregateToCombPass()); + pm.addPass(circt::createConvertCombToAIG()); + pm.addPass(createCSEPass()); + pm.addPass(createSimpleCanonicalizerPass()); + pm.addPass(createCSEPass()); +} + +void circt::synthesis::buildAIGOptimizationPipeline( + OpPassManager &pm, const AIGOptimizationPipelineOptions &options) { + + pm.addPass(aig::createLowerVariadic()); + + // TODO: LowerWordToBits is not scalable for large designs. Change to + // conditionally enable the pass once the rest of the pipeline was able + // to handle multibit operands properly. + pm.addPass(aig::createLowerWordToBits()); + pm.addPass(createCSEPass()); + pm.addPass(createSimpleCanonicalizerPass()); + + if (!options.abcCommands.empty()) { + aig::ABCRunnerOptions abcOptions; + abcOptions.abcPath = options.abcPath; + abcOptions.abcCommands.assign(options.abcCommands.begin(), + options.abcCommands.end()); + abcOptions.continueOnFailure = options.ignoreAbcFailures; + pm.addPass(aig::createABCRunner(abcOptions)); + } + // TODO: Add balancing, rewriting, FRAIG conversion, etc. +} + +//===----------------------------------------------------------------------===// +// Pipeline Registration +//===----------------------------------------------------------------------===// + +void circt::synthesis::registerSynthesisPipeline() { + PassPipelineRegistration( + "synthesis-aig-lowering-pipeline", + "The default pipeline for until AIG lowering", buildAIGLoweringPipeline); + PassPipelineRegistration( + "synthesis-aig-optimization-pipeline", + "The default pipeline for AIG optimization pipeline", + buildAIGOptimizationPipeline); +} diff --git a/tools/circt-synth/CMakeLists.txt b/tools/circt-synth/CMakeLists.txt index 504d7288b6..e1d9a83e3c 100644 --- a/tools/circt-synth/CMakeLists.txt +++ b/tools/circt-synth/CMakeLists.txt @@ -3,20 +3,17 @@ target_link_libraries(circt-synth PRIVATE CIRCTAIG CIRCTAIGToComb - CIRCTAIGTransforms CIRCTAIGAnalysis CIRCTComb - CIRCTCombToAIG CIRCTDebug CIRCTEmit CIRCTHW - CIRCTHWTransforms CIRCTLTL CIRCTOM CIRCTSeq CIRCTSim - CIRCTSupport CIRCTSV + CIRCTSynthesis CIRCTTransforms CIRCTVerif MLIRBytecodeWriter diff --git a/tools/circt-synth/circt-synth.cpp b/tools/circt-synth/circt-synth.cpp index d7e8d9a705..f3d31e2303 100644 --- a/tools/circt-synth/circt-synth.cpp +++ b/tools/circt-synth/circt-synth.cpp @@ -12,7 +12,6 @@ //===----------------------------------------------------------------------===// #include "circt/Conversion/AIGToComb.h" -#include "circt/Conversion/CombToAIG.h" #include "circt/Dialect/AIG/AIGDialect.h" #include "circt/Dialect/AIG/AIGPasses.h" #include "circt/Dialect/AIG/Analysis/LongestPathAnalysis.h" @@ -22,7 +21,6 @@ #include "circt/Dialect/Emit/EmitDialect.h" #include "circt/Dialect/HW/HWDialect.h" #include "circt/Dialect/HW/HWOps.h" -#include "circt/Dialect/HW/HWPasses.h" #include "circt/Dialect/LTL/LTLDialect.h" #include "circt/Dialect/OM/OMDialect.h" #include "circt/Dialect/SV/SVDialect.h" @@ -31,12 +29,14 @@ #include "circt/Dialect/Verif/VerifDialect.h" #include "circt/Support/Passes.h" #include "circt/Support/Version.h" +#include "circt/Synthesis/SynthesisPipeline.h" #include "circt/Transforms/Passes.h" #include "mlir/Bytecode/BytecodeWriter.h" #include "mlir/IR/Diagnostics.h" #include "mlir/IR/OwningOpRef.h" #include "mlir/Parser/Parser.h" #include "mlir/Pass/PassManager.h" +#include "mlir/Pass/PassRegistry.h" #include "mlir/Support/FileUtilities.h" #include "mlir/Support/LogicalResult.h" #include "mlir/Transforms/Passes.h" @@ -51,6 +51,7 @@ namespace cl = llvm::cl; using namespace mlir; using namespace circt; +using namespace synthesis; //===----------------------------------------------------------------------===// // Command-line options declaration @@ -90,7 +91,6 @@ static cl::opt cl::desc("Allow unknown dialects in the input"), cl::init(false), cl::cat(mainCategory)); -// Options to control early-out from pipeline. enum Until { UntilAIGLowering, UntilEnd }; static auto runUntilValues = llvm::cl::values( @@ -140,7 +140,7 @@ static cl::opt abcPath("abc-path", cl::desc("Path to ABC"), cl::cat(mainCategory)); static cl::opt - continueOnFailure("ignore-abc-failures", + ignoreAbcFailures("ignore-abc-failures", cl::desc("Continue on ABC failure instead of aborting"), cl::init(false), cl::cat(mainCategory)); @@ -152,6 +152,17 @@ static bool untilReached(Until until) { return until >= runUntilBefore || until > runUntilAfter; } +static void +nestOrAddToHierarchicalRunner(OpPassManager &pm, + std::function pipeline, + const std::string &topName) { + if (topName.empty()) { + pipeline(pm.nest()); + } else { + pm.addPass(circt::createHierarchicalRunner(topName, pipeline)); + } +} + //===----------------------------------------------------------------------===// // Tool implementation //===----------------------------------------------------------------------===// @@ -161,60 +172,24 @@ static void partiallyLegalizeCombToAIG(SmallVectorImpl &ops) { (ops.push_back(AllowedOpTy::getOperationName().str()), ...); } -static void populateSynthesisPipeline(PassManager &pm) { - auto pipeline = [](OpPassManager &mpm) { - // Add the AIG to Comb at the scope exit if requested. - auto addAIGToComb = llvm::make_scope_exit([&]() { - if (convertToComb) { - mpm.addPass(circt::createConvertAIGToComb()); - mpm.addPass(createCSEPass()); - } - }); - { - // Partially legalize Comb to AIG, run CSE and canonicalization. - circt::ConvertCombToAIGOptions options; - partiallyLegalizeCombToAIG( - options.additionalLegalOps); - mpm.addPass(circt::createConvertCombToAIG(options)); - } - mpm.addPass(createCSEPass()); - mpm.addPass(createSimpleCanonicalizerPass()); - - mpm.addPass(circt::hw::createHWAggregateToCombPass()); - mpm.addPass(circt::createConvertCombToAIG()); - mpm.addPass(createCSEPass()); +// Add a default synthesis pipeline and analysis. +static void populateCIRCTSynthPipeline(PassManager &pm) { + auto pipeline = [](OpPassManager &pm) { + circt::synthesis::buildAIGLoweringPipeline(pm); if (untilReached(UntilAIGLowering)) return; - mpm.addPass(createSimpleCanonicalizerPass()); - mpm.addPass(createCSEPass()); - mpm.addPass(aig::createLowerVariadic()); - // TODO: LowerWordToBits is not scalable for large designs. Change to - // conditionally enable the pass once the rest of the pipeline was able - // to handle multibit operands properly. - mpm.addPass(aig::createLowerWordToBits()); - mpm.addPass(createCSEPass()); - mpm.addPass(createSimpleCanonicalizerPass()); - if (!abcCommands.empty()) { - aig::ABCRunnerOptions options; - options.abcPath = abcPath; - options.abcCommands.assign(abcCommands.begin(), abcCommands.end()); - options.continueOnFailure = continueOnFailure; - mpm.addPass(aig::createABCRunner(options)); - } - // TODO: Add balancing, rewriting, FRAIG conversion, etc. - if (untilReached(UntilEnd)) - return; + + circt::synthesis::AIGOptimizationPipelineOptions options; + options.abcCommands = abcCommands; + options.abcPath.setValue(abcPath); + options.ignoreAbcFailures.setValue(ignoreAbcFailures); + + circt::synthesis::buildAIGOptimizationPipeline(pm, options); }; - if (topName.empty()) { - pipeline(pm.nest()); - } else { - pm.addPass(circt::createHierarchicalRunner(topName, pipeline)); - } + nestOrAddToHierarchicalRunner(pm, pipeline, topName); + // Run analysis if requested. if (!outputLongestPath.empty()) { circt::aig::PrintLongestPathAnalysisOptions options; options.outputFile = outputLongestPath; @@ -223,7 +198,14 @@ static void populateSynthesisPipeline(PassManager &pm) { pm.addPass(circt::aig::createPrintLongestPathAnalysis(options)); } - // TODO: Add LUT mapping, etc. + if (convertToComb) + nestOrAddToHierarchicalRunner( + pm, + [&](OpPassManager &pm) { + pm.addPass(circt::createConvertAIGToComb()); + pm.addPass(createCSEPass()); + }, + topName); } /// Check output stream before writing bytecode to it. @@ -285,7 +267,7 @@ static LogicalResult executeSynthesis(MLIRContext &context) { pm.addInstrumentation( std::make_unique>( "circt-synth")); - populateSynthesisPipeline(pm); + populateCIRCTSynthPipeline(pm); if (!topName.empty()) { // Set a top module name for the longest path analysis. @@ -320,6 +302,7 @@ int main(int argc, char **argv) { registerPassManagerCLOptions(); registerDefaultTimingManagerCLOptions(); registerAsmPrinterCLOptions(); + cl::AddExtraVersionPrinter( [](llvm::raw_ostream &os) { os << circt::getCirctVersion() << '\n'; });