diff --git a/include/circt/Dialect/SV/SVPasses.h b/include/circt/Dialect/SV/SVPasses.h index 6084319784f7..bac54e229d62 100644 --- a/include/circt/Dialect/SV/SVPasses.h +++ b/include/circt/Dialect/SV/SVPasses.h @@ -29,7 +29,9 @@ std::unique_ptr createHWMemSimImplPass(bool replSeqMem = false, bool ignoreReadEnableMem = false, bool stripMuxPragmas = false); -std::unique_ptr createSVExtractTestCodePass(); +std::unique_ptr +createSVExtractTestCodePass(bool disableInstanceExtraction = false, + bool disableModuleInlining = false); std::unique_ptr createHWExportModuleHierarchyPass(llvm::Optional directory = {}); /// Generate the code for registering passes. diff --git a/include/circt/Dialect/SV/SVPasses.td b/include/circt/Dialect/SV/SVPasses.td index 4b84e691f2d1..5e71b1c66ac6 100644 --- a/include/circt/Dialect/SV/SVPasses.td +++ b/include/circt/Dialect/SV/SVPasses.td @@ -114,6 +114,12 @@ def SVExtractTestCode : Pass<"sv-extract-test-code", "ModuleOp"> { let constructor = "circt::sv::createSVExtractTestCodePass()"; let dependentDialects = ["circt::sv::SVDialect"]; + let options = [ + Option<"disableInstanceExtraction", "disable-instance-extraction", "bool", + "false", "Disable extracting instances only that feed test code">, + Option<"disableModuleInlining", "disable-module-inlining", "bool", + "false", "Disable inlining modules that only feed test code"> + ]; let statistics = [ Statistic<"numOpsExtracted", "num-ops-extracted", "Number of ops extracted">, Statistic<"numOpsErased", "num-ops-erased", "Number of ops erased"> diff --git a/lib/Dialect/SV/Transforms/SVExtractTestCode.cpp b/lib/Dialect/SV/Transforms/SVExtractTestCode.cpp index 81c5ac9b1d64..35713cb37ba1 100644 --- a/lib/Dialect/SV/Transforms/SVExtractTestCode.cpp +++ b/lib/Dialect/SV/Transforms/SVExtractTestCode.cpp @@ -448,6 +448,11 @@ namespace { struct SVExtractTestCodeImplPass : public SVExtractTestCodeBase { + SVExtractTestCodeImplPass(bool disableInstanceExtraction, + bool disableModuleInlining) { + this->disableInstanceExtraction = disableInstanceExtraction; + this->disableModuleInlining = disableModuleInlining; + } void runOnOperation() override; private: @@ -490,7 +495,9 @@ struct SVExtractTestCodeImplPass // Find instances that directly feed the clone set, and add them if // possible. - addInstancesToCloneSet(inputs, opsToClone, opsToErase, extractedInstances); + if (!disableInstanceExtraction) + addInstancesToCloneSet(inputs, opsToClone, opsToErase, + extractedInstances); numOpsExtracted += opsToClone.size(); numOpsErased += opsToErase.size(); @@ -629,7 +636,7 @@ void SVExtractTestCodeImplPass::runOnOperation() { coverBindFile, bindTable, opsToErase); // Inline any modules that only have inputs for test code. - if (anyThingExtracted) + if (!disableModuleInlining && anyThingExtracted) inlineInputOnly(rtlmod, instanceGraph, bindTable, opsToErase); // Erase any instances that were extracted, and their forward dataflow. @@ -661,6 +668,9 @@ void SVExtractTestCodeImplPass::runOnOperation() { top->removeAttr("firrtl.extract.testbench"); } -std::unique_ptr circt::sv::createSVExtractTestCodePass() { - return std::make_unique(); +std::unique_ptr +circt::sv::createSVExtractTestCodePass(bool disableInstanceExtraction, + bool disableModuleInlining) { + return std::make_unique(disableInstanceExtraction, + disableModuleInlining); } diff --git a/test/firtool/extract-test-code.fir b/test/firtool/extract-test-code.fir new file mode 100644 index 000000000000..ea47318b8be5 --- /dev/null +++ b/test/firtool/extract-test-code.fir @@ -0,0 +1,57 @@ +; RUN: firtool %s -extract-test-code | FileCheck %s +; RUN: firtool %s -extract-test-code -etc-disable-instance-extraction | FileCheck %s --check-prefix=DISABLEINSTANCE +; RUN: firtool %s -extract-test-code -etc-disable-module-inlining | FileCheck %s --check-prefix=DISABLEMODULE + +circuit Top: + module Foo: + input a : UInt<1> + output b : UInt<1> + b <= a + + ; Ensure foo is extracted by default. + ; CHECK-LABEL: module InstanceExtracted_assert( + ; CHECK: Foo foo + + ; Ensure foo is not extracted when disabled. + ; DISABLEINSTANCE-LABEL: module InstanceExtracted( + ; DISABLEINSTANCE: Foo foo + + module InstanceExtracted: + input clock : Clock + input cond : UInt<1> + output out : UInt<1> + + wire b : UInt<1> + inst foo of Foo + foo.a <= cond + b <= foo.b + + assert(clock, cond, b, "Some assertion") + + out <= cond + + ; Ensure InputOnly is inlined by default. + ; CHECK-NOT: module InputOnly( + + ; Ensure InputOnly is not inlined when disabled. + ; DISABLEMODULE-LABEL: module InputOnly( + + module InputOnly: + input clock : Clock + input cond : UInt<1> + assert(clock, cond, cond, "Some assertion") + + module Top: + input clock : Clock + input cond : UInt<1> + output out : UInt<1> + + inst instance_extracted of InstanceExtracted + instance_extracted.clock <= clock + instance_extracted.cond <= cond + out <= instance_extracted.out + + inst input_only of InputOnly + input_only.clock <= clock + input_only.cond <= cond + diff --git a/tools/firtool/firtool.cpp b/tools/firtool/firtool.cpp index c3d6fc1f37ae..0990cb0b9ee6 100644 --- a/tools/firtool/firtool.cpp +++ b/tools/firtool/firtool.cpp @@ -312,6 +312,16 @@ static cl::opt disableInferRW("disable-infer-rw", cl::init(false), cl::Hidden, cl::cat(mainCategory)); +static cl::opt etcDisableInstanceExtraction( + "etc-disable-instance-extraction", + cl::desc("Disable extracting instances only that feed test code"), + cl::init(false), cl::cat(mainCategory)); + +static cl::opt etcDisableModuleInlining( + "etc-disable-module-inlining", + cl::desc("Disable inlining modules that only feed test code"), + cl::init(false), cl::cat(mainCategory)); + enum OutputFormatKind { OutputParseOnly, OutputIRFir, @@ -751,7 +761,8 @@ processBuffer(MLIRContext &context, TimingScope &ts, llvm::SourceMgr &sourceMgr, stripMuxPragmas)); if (extractTestCode) - pm.addPass(sv::createSVExtractTestCodePass()); + pm.addPass(sv::createSVExtractTestCodePass(etcDisableInstanceExtraction, + etcDisableModuleInlining)); // If enabled, run the optimizer. if (!disableOptimization) {