diff --git a/docs/Dialects/FIRRTL/FIRRTLAnnotations.md b/docs/Dialects/FIRRTL/FIRRTLAnnotations.md index 90e8e2361074..d5cb8654cc2b 100644 --- a/docs/Dialects/FIRRTL/FIRRTLAnnotations.md +++ b/docs/Dialects/FIRRTL/FIRRTLAnnotations.md @@ -1409,38 +1409,3 @@ objects, with each object containing the following members: - `module_name` - A string describing the name of the module. - `instances` - An array of objects, where each object is a direct instance within the current module. - -### firrtl.extract.assert - -Used by SVExtractTestCode. Specifies the output directory for extracted -modules. This attribute has type `OutputFileAttr`. - -### firrtl.extract.assume - -Used by SVExtractTestCode. Specifies the output directory for extracted -modules. This attribute has type `OutputFileAttr`. - -### firrtl.extract.cover - -Used by SVExtractTestCode. Specifies the output directory for extracted -modules. This attribute has type `OutputFileAttr`. - -### firrtl.extract.assert.bindfile - -Used by SVExtractTestCode. Specifies the output file for extracted -modules' bind file. This attribute has type `OutputFileAttr`. - -### firrtl.extract.assume.bindfile - -Used by SVExtractTestCode. Specifies the output file for extracted -modules' bind file. This attribute has type `OutputFileAttr`. - -### firrtl.extract.cover.bindfile - -Used by SVExtractTestCode. Specifies the output file for extracted -modules' bind file. This attribute has type `OutputFileAttr`. - -### firrtl.extract.[cover|assume|assert].extra - -Used by SVExtractTestCode. Indicates a module whose instances should be -extracted from the circuit in the indicated extraction type. diff --git a/include/circt-c/Firtool/Firtool.h b/include/circt-c/Firtool/Firtool.h index 140adda59b5a..304e6299d708 100644 --- a/include/circt-c/Firtool/Firtool.h +++ b/include/circt-c/Firtool/Firtool.h @@ -147,10 +147,6 @@ MLIR_CAPI_EXPORTED void circtFirtoolOptionsSetReplSeqMemFile(CirctFirtoolFirtoolOptions options, MlirStringRef value); -MLIR_CAPI_EXPORTED void -circtFirtoolOptionsSetExtractTestCode(CirctFirtoolFirtoolOptions options, - bool value); - MLIR_CAPI_EXPORTED void circtFirtoolOptionsSetIgnoreReadEnableMem(CirctFirtoolFirtoolOptions options, bool value); diff --git a/include/circt/Dialect/SV/SVPasses.h b/include/circt/Dialect/SV/SVPasses.h index 56e303a705bf..fc97df20e69f 100644 --- a/include/circt/Dialect/SV/SVPasses.h +++ b/include/circt/Dialect/SV/SVPasses.h @@ -31,10 +31,6 @@ std::unique_ptr createHWLowerInstanceChoices(); std::unique_ptr createHWGeneratorCalloutPass(); std::unique_ptr createHWEliminateInOutPortsPass( const HWEliminateInOutPortsOptions &options = {}); -std::unique_ptr -createSVExtractTestCodePass(bool disableInstanceExtraction = false, - bool disableRegisterExtraction = false, - bool disableModuleInlining = false); std::unique_ptr createHWExportModuleHierarchyPass(); /// Generate the code for registering passes. #define GEN_PASS_REGISTRATION diff --git a/include/circt/Dialect/SV/SVPasses.td b/include/circt/Dialect/SV/SVPasses.td index 4ddd92d57374..1e2e117447ee 100644 --- a/include/circt/Dialect/SV/SVPasses.td +++ b/include/circt/Dialect/SV/SVPasses.td @@ -88,33 +88,6 @@ def HWGeneratorCalloutPass : Pass<"hw-generator-callout", "ModuleOp"> { ]; } -def SVExtractTestCode : Pass<"sv-extract-test-code", "ModuleOp"> { - let summary = "Extract simulation only constructs to modules and bind"; - let description = [{ - This pass extracts cover, assume, assert operations to a module, along with - any ops feeding them only, to modules which are instantiated with a bind - statement. - }]; - - let constructor = "circt::sv::createSVExtractTestCodePass()"; - let dependentDialects = [ - "circt::sv::SVDialect", - "circt::verif::VerifDialect" - ]; - let options = [ - Option<"disableInstanceExtraction", "disable-instance-extraction", "bool", - "false", "Disable extracting instances only that feed test code">, - Option<"disableRegisterExtraction", "disable-register-extraction", "bool", - "false", "Disable extracting registers 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"> - ]; -} - def SVTraceIVerilog : Pass<"sv-trace-iverilog", "ModuleOp"> { let summary = "Add tracing to an iverilog simulated module"; let description = [{ diff --git a/include/circt/Firtool/Firtool.h b/include/circt/Firtool/Firtool.h index 052d04a38844..da0b32d6e85d 100644 --- a/include/circt/Firtool/Firtool.h +++ b/include/circt/Firtool/Firtool.h @@ -129,7 +129,6 @@ class FirtoolOptions { bool shouldAddVivadoRAMAddressConflictSynthesisBugWorkaround() const { return addVivadoRAMAddressConflictSynthesisBugWorkaround; } - bool shouldExtractTestCode() const { return extractTestCode; } bool shouldFixupEICGWrapper() const { return fixupEICGWrapper; } bool shouldDisableCSEinClasses() const { return disableCSEinClasses; } bool shouldSelectDefaultInstanceChoice() const { @@ -258,11 +257,6 @@ class FirtoolOptions { return *this; } - FirtoolOptions &setExtractTestCode(bool value) { - extractTestCode = value; - return *this; - } - FirtoolOptions &setIgnoreReadEnableMem(bool value) { ignoreReadEnableMem = value; return *this; @@ -420,7 +414,6 @@ class FirtoolOptions { std::string blackBoxRootPath; bool replSeqMem; std::string replSeqMemFile; - bool extractTestCode; bool ignoreReadEnableMem; RandomKind disableRandom; std::string outputAnnotationFilename; diff --git a/lib/CAPI/Firtool/Firtool.cpp b/lib/CAPI/Firtool/Firtool.cpp index 118004fa359b..10191d351047 100644 --- a/lib/CAPI/Firtool/Firtool.cpp +++ b/lib/CAPI/Firtool/Firtool.cpp @@ -199,11 +199,6 @@ void circtFirtoolOptionsSetReplSeqMemFile(CirctFirtoolFirtoolOptions options, unwrap(options)->setReplSeqMemFile(unwrap(value)); } -void circtFirtoolOptionsSetExtractTestCode(CirctFirtoolFirtoolOptions options, - bool value) { - unwrap(options)->setExtractTestCode(value); -} - void circtFirtoolOptionsSetIgnoreReadEnableMem( CirctFirtoolFirtoolOptions options, bool value) { unwrap(options)->setIgnoreReadEnableMem(value); diff --git a/lib/Conversion/FIRRTLToHW/LowerToHW.cpp b/lib/Conversion/FIRRTLToHW/LowerToHW.cpp index 0d7e307d6cb4..0bd62b963868 100644 --- a/lib/Conversion/FIRRTLToHW/LowerToHW.cpp +++ b/lib/Conversion/FIRRTLToHW/LowerToHW.cpp @@ -159,33 +159,6 @@ static Value castFromFIRRTLType(Value val, Type type, return val; } -/// Move a ExtractTestCode related annotation from annotations to an attribute. -static void moveVerifAnno(ModuleOp top, AnnotationSet &annos, - StringRef annoClass, StringRef attrBase) { - auto anno = annos.getAnnotation(annoClass); - auto *ctx = top.getContext(); - if (!anno) - return; - if (auto dir = anno.getMember("directory")) { - SmallVector old; - for (auto i : top->getAttrs()) - old.push_back(i); - old.emplace_back( - StringAttr::get(ctx, attrBase), - hw::OutputFileAttr::getAsDirectory(ctx, dir.getValue(), true, true)); - top->setAttrs(old); - } - if (auto file = anno.getMember("filename")) { - SmallVector old; - for (auto i : top->getAttrs()) - old.push_back(i); - old.emplace_back(StringAttr::get(ctx, attrBase + ".bindfile"), - hw::OutputFileAttr::getFromFilename( - ctx, file.getValue(), /*excludeFromFileList=*/true)); - top->setAttrs(old); - } -} - static unsigned getBitWidthFromVectorSize(unsigned size) { return size == 1 ? 1 : llvm::Log2_64_Ceil(size); } @@ -655,15 +628,6 @@ void FIRRTLModuleLowering::runOnOperation() { SmallVector opsToProcess; AnnotationSet circuitAnno(circuit); - moveVerifAnno(getOperation(), circuitAnno, extractAssertAnnoClass, - "firrtl.extract.assert"); - moveVerifAnno(getOperation(), circuitAnno, extractAssumeAnnoClass, - "firrtl.extract.assume"); - moveVerifAnno(getOperation(), circuitAnno, extractCoverageAnnoClass, - "firrtl.extract.cover"); - circuitAnno.removeAnnotationsWithClass( - extractAssertAnnoClass, extractAssumeAnnoClass, extractCoverageAnnoClass); - state.processRemainingAnnotations(circuit, circuitAnno); // Iterate through each operation in the circuit body, transforming any // FModule's we come across. If any module fails to lower, return early. diff --git a/lib/Dialect/SV/Transforms/CMakeLists.txt b/lib/Dialect/SV/Transforms/CMakeLists.txt index a86a4ee8cde9..f47141211018 100644 --- a/lib/Dialect/SV/Transforms/CMakeLists.txt +++ b/lib/Dialect/SV/Transforms/CMakeLists.txt @@ -4,7 +4,6 @@ add_circt_dialect_library(CIRCTSVTransforms HWStubExternalModules.cpp HWLegalizeModules.cpp PrettifyVerilog.cpp - SVExtractTestCode.cpp HWExportModuleHierarchy.cpp SVTraceIVerilog.cpp HWEliminateInOutPorts.cpp diff --git a/lib/Dialect/SV/Transforms/SVExtractTestCode.cpp b/lib/Dialect/SV/Transforms/SVExtractTestCode.cpp deleted file mode 100644 index a69d84a9ccef..000000000000 --- a/lib/Dialect/SV/Transforms/SVExtractTestCode.cpp +++ /dev/null @@ -1,894 +0,0 @@ -//===- SVExtractTestCode.cpp - SV Simulation Extraction Pass --------------===// -// -// 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 transformation pass extracts simulation constructs to submodules. It -// will take simulation operations, write, finish, assert, assume, and cover and -// extract them and the dataflow into them into a separate module. This module -// is then instantiated in the original module. -// -//===----------------------------------------------------------------------===// - -#include "circt/Dialect/Emit/EmitOps.h" -#include "circt/Dialect/HW/HWAttributes.h" -#include "circt/Dialect/HW/HWInstanceGraph.h" -#include "circt/Dialect/HW/HWOps.h" -#include "circt/Dialect/HW/HWSymCache.h" -#include "circt/Dialect/HW/InnerSymbolNamespace.h" -#include "circt/Dialect/SV/SVOps.h" -#include "circt/Dialect/SV/SVPasses.h" -#include "circt/Dialect/Seq/SeqDialect.h" -#include "circt/Dialect/Seq/SeqOps.h" -#include "circt/Dialect/Verif/VerifOps.h" -#include "circt/Support/Namespace.h" -#include "mlir/IR/Builders.h" -#include "mlir/IR/IRMapping.h" -#include "mlir/Pass/Pass.h" -#include "llvm/ADT/SetVector.h" - -#include - -namespace circt { -namespace sv { -#define GEN_PASS_DEF_SVEXTRACTTESTCODE -#include "circt/Dialect/SV/SVPasses.h.inc" -} // namespace sv -} // namespace circt - -using namespace mlir; -using namespace circt; -using namespace sv; - -using BindTable = DenseMap>; - -//===----------------------------------------------------------------------===// -// StubExternalModules Helpers -//===----------------------------------------------------------------------===// - -// Reimplemented from SliceAnalysis to use a worklist rather than recursion and -// non-insert ordered set. Implement this as a DFS and not a BFS so that the -// order is stable across changes to intermediary operations. (It is then -// necessary to use the _operands_ as a worklist and not the _operations_.) -static void -getBackwardSliceSimple(Operation *rootOp, SetVector &backwardSlice, - llvm::function_ref filter) { - SmallVector worklist(rootOp->getOperands()); - - while (!worklist.empty()) { - Value operand = worklist.pop_back_val(); - Operation *definingOp = operand.getDefiningOp(); - - if (!definingOp || - definingOp->hasTrait()) - continue; - - // Evaluate whether we should keep this def. - // This is useful in particular to implement scoping; i.e. return the - // transitive backwardSlice in the current scope. - if (filter && !filter(definingOp)) - continue; - - if (definingOp) { - if (!backwardSlice.contains(definingOp)) - for (auto newOperand : llvm::reverse(definingOp->getOperands())) - worklist.push_back(newOperand); - } else if (auto blockArg = dyn_cast(operand)) { - Block *block = blockArg.getOwner(); - Operation *parentOp = block->getParentOp(); - // TODO: determine whether we want to recurse backward into the other - // blocks of parentOp, which are not technically backward unless they - // flow into us. For now, just bail. - assert(parentOp->getNumRegions() == 1 && - parentOp->getRegion(0).getBlocks().size() == 1); - if (!backwardSlice.contains(parentOp)) - for (auto newOperand : llvm::reverse(parentOp->getOperands())) - worklist.push_back(newOperand); - } else { - llvm_unreachable("No definingOp and not a block argument."); - } - - backwardSlice.insert(definingOp); - } -} - -// Compute the ops defining the blocks a set of ops are in. -static void blockSlice(SetVector &ops, - SetVector &blocks) { - for (auto op : ops) { - while (!isa(op->getParentOp())) { - op = op->getParentOp(); - blocks.insert(op); - } - } -} - -static void computeSlice(SetVector &roots, - SetVector &results, - llvm::function_ref filter) { - for (auto *op : roots) - getBackwardSliceSimple(op, results, filter); -} - -// Return a backward slice started from `roots` until dataflow reaches to an -// operations for which `filter` returns false. -static SetVector -getBackwardSlice(SetVector &roots, - llvm::function_ref filter) { - SetVector results; - computeSlice(roots, results, filter); - - // Get Blocks - SetVector blocks; - blockSlice(roots, blocks); - blockSlice(results, blocks); - - // Make sure dataflow to block args (if conds, etc) is included - computeSlice(blocks, results, filter); - - results.insert(roots.begin(), roots.end()); - results.insert(blocks.begin(), blocks.end()); - return results; -} - -// Return a backward slice started from opertaions for which `rootFn` returns -// true. -static SetVector -getBackwardSlice(hw::HWModuleOp module, - llvm::function_ref rootFn, - llvm::function_ref filterFn) { - SetVector roots; - module.walk([&](Operation *op) { - if (!isa(op) && rootFn(op)) - roots.insert(op); - }); - return getBackwardSlice(roots, filterFn); -} - -static StringAttr getNameForPort(Value val, - SmallVector modulePorts) { - if (auto bv = dyn_cast(val)) - return cast(modulePorts[bv.getArgNumber()]); - - if (auto *op = val.getDefiningOp()) { - if (auto readinout = dyn_cast(op)) { - if (auto *readOp = readinout.getInput().getDefiningOp()) { - if (auto wire = dyn_cast(readOp)) - return wire.getNameAttr(); - if (auto reg = dyn_cast(readOp)) - return reg.getNameAttr(); - } - } else if (auto inst = dyn_cast(op)) { - auto index = cast(val).getResultNumber(); - SmallString<64> portName = inst.getInstanceName(); - portName += "."; - auto resultName = inst.getOutputName(index); - if (resultName && !resultName.getValue().empty()) - portName += resultName.getValue(); - else - Twine(index).toVector(portName); - return StringAttr::get(val.getContext(), portName); - } else if (op->getNumResults() == 1) { - if (auto name = op->getAttrOfType("name")) - return name; - if (auto namehint = op->getAttrOfType("sv.namehint")) - return namehint; - } - } - - return StringAttr::get(val.getContext(), ""); -} - -// Given a set of values, construct a module and bind instance of that module -// that passes those values through. Returns the new module and the instance -// pointing to it. -static hw::HWModuleOp createModuleForCut(hw::HWModuleOp op, - SetVector &inputs, - IRMapping &cutMap, StringRef suffix, - Attribute path, Attribute fileName, - BindTable &bindTable) { - // Filter duplicates and track duplicate reads of elements so we don't - // make ports for them - SmallVector realInputs; - DenseMap dups; // wire,reg,lhs -> read - DenseMap> - realReads; // port mapped read -> dup reads - for (auto v : inputs) { - if (auto readinout = dyn_cast_or_null(v.getDefiningOp())) { - auto op = readinout.getInput(); - if (dups.count(op)) { - realReads[dups[op]].push_back(v); - continue; - } - dups[op] = v; - } - realInputs.push_back(v); - } - - // Create the extracted module right next to the original one. - OpBuilder b(op); - - // Construct the ports, this is just the input Values - SmallVector ports; - { - Namespace portNames; - auto srcPorts = op.getInputNames(); - for (auto port : llvm::enumerate(realInputs)) { - auto name = getNameForPort(port.value(), srcPorts).getValue(); - name = portNames.newName(name.empty() ? "port_" + Twine(port.index()) - : name); - ports.push_back({{b.getStringAttr(name), port.value().getType(), - hw::ModulePort::Direction::Input}, - port.index()}); - } - } - - // Create the module, setting the output path if indicated. - auto newMod = hw::HWModuleOp::create( - b, op.getLoc(), - b.getStringAttr(getVerilogModuleNameAttr(op).getValue() + suffix), ports); - if (path) - newMod->setAttr("output_file", path); - if (auto fragments = op->getAttr(emit::getFragmentsAttrName())) - newMod->setAttr(emit::getFragmentsAttrName(), fragments); - newMod.setCommentAttr(b.getStringAttr("VCS coverage exclude_file")); - newMod.setPrivate(); - - // Update the mapping from old values to cloned values - for (auto port : llvm::enumerate(realInputs)) { - cutMap.map(port.value(), newMod.getBody().getArgument(port.index())); - for (auto extra : realReads[port.value()]) - cutMap.map(extra, newMod.getBody().getArgument(port.index())); - } - cutMap.map(op.getBodyBlock(), newMod.getBodyBlock()); - - // Add an instance in the old module for the extracted module - b = OpBuilder::atBlockTerminator(op.getBodyBlock()); - auto inst = hw::InstanceOp::create( - b, op.getLoc(), newMod, newMod.getName(), realInputs, ArrayAttr(), - hw::InnerSymAttr::get(b.getStringAttr( - ("__ETC_" + getVerilogModuleNameAttr(op).getValue() + suffix) - .str()))); - inst.setDoNotPrintAttr(b.getUnitAttr()); - b = OpBuilder::atBlockEnd( - &op->getParentOfType()->getRegion(0).front()); - - auto bindOp = sv::BindOp::create(b, op.getLoc(), op.getNameAttr(), - inst.getInnerSymAttr().getSymName()); - bindTable[op.getNameAttr()][inst.getInnerSymAttr().getSymName()] = bindOp; - if (fileName) - bindOp->setAttr("output_file", fileName); - return newMod; -} - -// Some blocks have terminators, some don't -static void setInsertPointToEndOrTerminator(OpBuilder &builder, Block *block) { - if (!block->empty() && isa(block->getParentOp())) - builder.setInsertionPoint(&block->back()); - else - builder.setInsertionPointToEnd(block); -} - -// Shallow clone, which we use to not clone the content of blocks, doesn't -// clone the regions, so create all the blocks we need and update the mapping. -static void addBlockMapping(IRMapping &cutMap, Operation *oldOp, - Operation *newOp) { - assert(oldOp->getNumRegions() == newOp->getNumRegions()); - for (size_t i = 0, e = oldOp->getNumRegions(); i != e; ++i) { - auto &oldRegion = oldOp->getRegion(i); - auto &newRegion = newOp->getRegion(i); - for (auto oi = oldRegion.begin(), oe = oldRegion.end(); oi != oe; ++oi) { - cutMap.map(&*oi, &newRegion.emplaceBlock()); - } - } -} - -// Check if op has any operand using a value that isn't yet defined. -static bool hasOoOArgs(hw::HWModuleOp newMod, Operation *op) { - for (auto arg : op->getOperands()) { - auto *argOp = arg.getDefiningOp(); // may be null - if (!argOp) - continue; - if (argOp->getParentOfType() != newMod) - return true; - } - return false; -} - -// Update any operand which was emitted before its defining op was. -static void updateOoOArgs(SmallVectorImpl &lateBoundOps, - IRMapping &cutMap) { - for (auto *op : lateBoundOps) - for (unsigned argidx = 0, e = op->getNumOperands(); argidx < e; ++argidx) { - Value arg = op->getOperand(argidx); - if (cutMap.contains(arg)) - op->setOperand(argidx, cutMap.lookup(arg)); - } -} - -// Do the cloning, which is just a pre-order traversal over the module looking -// for marked ops. -static void migrateOps(hw::HWModuleOp oldMod, hw::HWModuleOp newMod, - SetVector &depOps, IRMapping &cutMap, - hw::InstanceGraph &instanceGraph) { - igraph::InstanceGraphNode *newModNode = instanceGraph.lookup(newMod); - SmallVector lateBoundOps; - OpBuilder b = OpBuilder::atBlockBegin(newMod.getBodyBlock()); - oldMod.walk([&](Operation *op) { - if (depOps.count(op)) { - setInsertPointToEndOrTerminator(b, cutMap.lookup(op->getBlock())); - auto newOp = b.cloneWithoutRegions(*op, cutMap); - addBlockMapping(cutMap, op, newOp); - if (hasOoOArgs(newMod, newOp)) - lateBoundOps.push_back(newOp); - if (auto instance = dyn_cast(op)) { - igraph::InstanceGraphNode *instMod = - instanceGraph.lookup(instance.getModuleNameAttr().getAttr()); - newModNode->addInstance(instance, instMod); - } - } - }); - updateOoOArgs(lateBoundOps, cutMap); -} - -// Check if the module has already been bound. -static bool isBound(hw::HWModuleLike op, hw::InstanceGraph &instanceGraph) { - auto *node = instanceGraph.lookup(op); - return llvm::any_of(node->uses(), [](igraph::InstanceRecord *a) { - auto inst = a->getInstance(); - if (!inst) - return false; - - return inst.getDoNotPrint(); - }); -} - -// Add any existing bindings to the bind table. -static void addExistingBinds(Block *topLevelModule, BindTable &bindTable) { - for (auto bind : topLevelModule->getOps()) { - hw::InnerRefAttr boundRef = bind.getInstance(); - bindTable[boundRef.getModule()][boundRef.getName()] = bind; - } -} - -// Inline any modules that only have inputs for test code. -static void -inlineInputOnly(hw::HWModuleOp oldMod, hw::InstanceGraph &instanceGraph, - BindTable &bindTable, SmallPtrSetImpl &opsToErase, - llvm::DenseSet &innerRefUsedByNonBindOp) { - - // Check if the module only has inputs. - if (oldMod.getNumOutputPorts() != 0) - return; - - // Check if it's ok to inline. We cannot inline the module if there exists a - // declaration with an inner symbol referred by non-bind ops (e.g. hierpath). - auto oldModName = oldMod.getModuleNameAttr(); - for (auto port : oldMod.getPortList()) { - auto sym = port.getSym(); - if (sym) { - for (auto property : sym) { - auto innerRef = hw::InnerRefAttr::get(oldModName, property.getName()); - if (innerRefUsedByNonBindOp.count(innerRef)) { - oldMod.emitWarning() << "module " << oldMod.getModuleName() - << " is an input only module but cannot " - "be inlined because a signal " - << port.name << " is referred by name"; - return; - } - } - } - } - - for (auto op : oldMod.getBodyBlock()->getOps()) { - if (auto innerSym = op.getInnerSymAttr()) { - for (auto property : innerSym) { - auto innerRef = hw::InnerRefAttr::get(oldModName, property.getName()); - if (innerRefUsedByNonBindOp.count(innerRef)) { - op.emitWarning() << "module " << oldMod.getModuleName() - << " is an input only module but cannot be inlined " - "because signals are referred by name"; - return; - } - } - } - } - - // Get the instance graph node for the old module. - igraph::InstanceGraphNode *node = instanceGraph.lookup(oldMod); - assert(!node->noUses() && - "expected module for inlining to be instantiated at least once"); - - // Iterate through each instance of the module. - OpBuilder b(oldMod); - bool allInlined = true; - for (igraph::InstanceRecord *use : llvm::make_early_inc_range(node->uses())) { - // If there is no instance, move on. - auto instLike = use->getInstance(); - if (!instLike) { - allInlined = false; - continue; - } - - // If the instance had a symbol, we can't inline it without more work. - hw::InstanceOp inst = cast(instLike.getOperation()); - if (inst.getInnerSym().has_value()) { - allInlined = false; - auto diag = - oldMod.emitWarning() - << "module " << oldMod.getModuleName() - << " cannot be inlined because there is an instance with a symbol"; - diag.attachNote(inst.getLoc()); - continue; - } - - // Build a mapping from module block arguments to instance inputs. - IRMapping mapping; - assert(inst.getInputs().size() == oldMod.getNumInputPorts()); - auto inputPorts = oldMod.getBodyBlock()->getArguments(); - for (size_t i = 0, e = inputPorts.size(); i < e; ++i) - mapping.map(inputPorts[i], inst.getOperand(i)); - - // Inline the body at the instantiation site. - hw::HWModuleOp instParent = - cast(use->getParent()->getModule()); - igraph::InstanceGraphNode *instParentNode = - instanceGraph.lookup(instParent); - SmallVector lateBoundOps; - b.setInsertionPoint(inst); - // Namespace that tracks inner symbols in the parent module. - hw::InnerSymbolNamespace nameSpace(instParent); - // A map from old inner symbols to new ones. - DenseMap symMapping; - - for (auto &op : *oldMod.getBodyBlock()) { - // If the op was erased by instance extraction, don't copy it over. - if (opsToErase.contains(&op)) - continue; - - // If the op has an inner sym, first create a new inner sym for it. - if (auto innerSymOp = dyn_cast(op)) { - if (auto innerSym = innerSymOp.getInnerSymAttr()) { - for (auto property : innerSym) { - auto oldName = property.getName(); - auto newName = - b.getStringAttr(nameSpace.newName(oldName.getValue())); - auto result = symMapping.insert({oldName, newName}); - (void)result; - assert(result.second && "inner symbols must be unique"); - } - } - } - - // For instances in the bind table, update the bind with the new parent. - if (auto innerInst = dyn_cast(op)) { - if (auto innerInstSym = innerInst.getInnerSymAttr()) { - auto it = - bindTable[oldMod.getNameAttr()].find(innerInstSym.getSymName()); - if (it != bindTable[oldMod.getNameAttr()].end()) { - sv::BindOp bind = it->second; - auto oldInnerRef = bind.getInstanceAttr(); - auto it = symMapping.find(oldInnerRef.getName()); - assert(it != symMapping.end() && - "inner sym mapping must be already populated"); - auto newName = it->second; - auto newInnerRef = - hw::InnerRefAttr::get(instParent.getModuleNameAttr(), newName); - OpBuilder::InsertionGuard g(b); - // Clone bind operations. - b.setInsertionPoint(bind); - sv::BindOp clonedBind = cast(b.clone(*bind, mapping)); - clonedBind.setInstanceAttr(newInnerRef); - bindTable[instParent.getModuleNameAttr()][newName] = - cast(clonedBind); - } - } - } - - // For all ops besides the output, clone into the parent body. - if (!isa(op)) { - Operation *clonedOp = b.clone(op, mapping); - // If some of the operands haven't been cloned over yet, due to cycles, - // remember to revisit this op. - if (hasOoOArgs(instParent, clonedOp)) - lateBoundOps.push_back(clonedOp); - - // If the cloned op is an instance, record it within the new parent in - // the instance graph. - if (auto innerInst = dyn_cast(clonedOp)) { - igraph::InstanceGraphNode *innerInstModule = - instanceGraph.lookup(innerInst.getModuleNameAttr().getAttr()); - instParentNode->addInstance(innerInst, innerInstModule); - } - - // If the cloned op has an inner sym, then attach an updated inner sym. - if (auto innerSymOp = dyn_cast(clonedOp)) { - if (auto oldInnerSym = innerSymOp.getInnerSymAttr()) { - SmallVector properties; - for (auto property : oldInnerSym) { - auto newSymName = symMapping[property.getName()]; - properties.push_back(hw::InnerSymPropertiesAttr::get( - op.getContext(), newSymName, property.getFieldID(), - property.getSymVisibility())); - } - auto innerSym = hw::InnerSymAttr::get(op.getContext(), properties); - innerSymOp.setInnerSymbolAttr(innerSym); - } - } - } - } - - // Map over any ops that didn't have their operands mapped when cloned. - updateOoOArgs(lateBoundOps, mapping); - - // Erase the old instantiation site. - assert(inst.use_empty() && "inlined instance should have no uses"); - use->erase(); - opsToErase.insert(inst); - } - - // If all instances were inlined, remove the module. - if (allInlined) { - // Erase old bind statements. - for (auto [_, bind] : bindTable[oldMod.getNameAttr()]) - bind.erase(); - bindTable[oldMod.getNameAttr()].clear(); - instanceGraph.erase(node); - opsToErase.insert(oldMod); - } -} - -static bool isAssertOp(hw::HWSymbolCache &symCache, Operation *op) { - // Symbols not in the cache will only be fore instances added by an extract - // phase and are not instances that could possibly have extract flags on them. - if (auto inst = dyn_cast(op)) - if (auto *mod = symCache.getDefinition(inst.getModuleNameAttr())) - if (mod->getAttr("firrtl.extract.assert.extra")) - return true; - - // If the format of assert is "ifElseFatal", PrintOp is lowered into - // ErrorOp. So we have to check message contents whether they encode - // verifications. See FIRParserAsserts for more details. - if (auto error = dyn_cast(op)) { - if (auto message = error.getMessage()) - return message->starts_with("assert:") || - message->starts_with("assert failed (verification library)") || - message->starts_with("Assertion failed") || - message->starts_with("assertNotX:") || - message->contains("[verif-library-assert]"); - return false; - } - - return isa(op); -} - -static bool isCoverOp(hw::HWSymbolCache &symCache, Operation *op) { - // Symbols not in the cache will only be fore instances added by an extract - // phase and are not instances that could possibly have extract flags on them. - if (auto inst = dyn_cast(op)) - if (auto *mod = symCache.getDefinition(inst.getModuleNameAttr())) - if (mod->getAttr("firrtl.extract.cover.extra")) - return true; - return isa( - op); -} - -static bool isAssumeOp(hw::HWSymbolCache &symCache, Operation *op) { - // Symbols not in the cache will only be fore instances added by an extract - // phase and are not instances that could possibly have extract flags on them. - if (auto inst = dyn_cast(op)) - if (auto *mod = symCache.getDefinition(inst.getModuleNameAttr())) - if (mod->getAttr("firrtl.extract.assume.extra")) - return true; - - return isa(op); -} - -/// Return true if the operation belongs to the design. -bool isInDesign(hw::HWSymbolCache &symCache, Operation *op, - bool disableInstanceExtraction = false, - bool disableRegisterExtraction = false) { - - // Module outputs are marked as designs. - if (isa(op)) - return true; - - // If an op has an innner sym, don't extract. - if (auto innerSymOp = dyn_cast(op)) - if (auto innerSym = innerSymOp.getInnerSymAttr()) - if (!innerSym.empty()) - return true; - - // Check whether the operation is a verification construct. Instance op could - // be used as verification construct so make sure to check this property - // first. - if (isAssertOp(symCache, op) || isCoverOp(symCache, op) || - isAssumeOp(symCache, op)) - return false; - - // For instances and regiseters, check by passed arguments. - if (isa(op)) - return disableInstanceExtraction; - if (isa(op)) - return disableRegisterExtraction; - - // Since we are not tracking dataflow through SV assignments, and we don't - // extract SV declarations (e.g. wire, reg or logic), so just read is part of - // the design. - if (isa(op)) - return true; - - // If the op has regions, we visit sub-regions later. - if (op->getNumRegions() > 0) - return false; - - // Special case some operations which we want to clone. - if (isa(op)) - return false; - - // Otherwise, operations with memory effects as a part design. - return !mlir::isMemoryEffectFree(op); -} - -//===----------------------------------------------------------------------===// -// StubExternalModules Pass -//===----------------------------------------------------------------------===// - -namespace { - -struct SVExtractTestCodeImplPass - : public circt::sv::impl::SVExtractTestCodeBase { - SVExtractTestCodeImplPass(bool disableInstanceExtraction, - bool disableRegisterExtraction, - bool disableModuleInlining) { - this->disableInstanceExtraction = disableInstanceExtraction; - this->disableRegisterExtraction = disableRegisterExtraction; - this->disableModuleInlining = disableModuleInlining; - } - void runOnOperation() override; - -private: - // Run the extraction on a module, and return true if test code was extracted. - bool doModule(hw::HWModuleOp module, llvm::function_ref fn, - StringRef suffix, Attribute path, Attribute bindFile, - BindTable &bindTable, SmallPtrSetImpl &opsToErase, - SetVector &opsInDesign) { - bool hasError = false; - // Find Operations of interest. - SetVector roots; - module->walk([&fn, &roots, &hasError](Operation *op) { - if (fn(op)) { - roots.insert(op); - if (op->getNumResults()) { - op->emitError("Extracting op with result"); - hasError = true; - } - } - }); - if (hasError) { - signalPassFailure(); - return false; - } - // No Ops? No problem. - if (roots.empty()) - return false; - - // Find the data-flow and structural ops to clone. Result includes roots. - // Track dataflow until it reaches to design parts except for constants that - // can be cloned freely. - auto opsToClone = getBackwardSlice(roots, [&](Operation *op) { - return !opsInDesign.count(op) || - op->hasTrait(); - }); - - // Find the dataflow into the clone set - SetVector inputs; - for (auto *op : opsToClone) { - for (auto arg : op->getOperands()) { - auto argOp = arg.getDefiningOp(); // may be null - if (!opsToClone.count(argOp)) - inputs.insert(arg); - } - // Erase cloned operations. - opsToErase.insert(op); - } - - numOpsExtracted += opsToClone.size(); - - // Make a module to contain the clone set, with arguments being the cut - IRMapping cutMap; - auto bmod = createModuleForCut(module, inputs, cutMap, suffix, path, - bindFile, bindTable); - - // Register the newly created module in the instance graph. - instanceGraph->addHWModule(bmod); - - // do the clone - migrateOps(module, bmod, opsToClone, cutMap, *instanceGraph); - - // erase old operations of interest eagerly, removing from erase set. - for (auto *op : roots) { - opsToErase.erase(op); - op->erase(); - } - - return true; - } - - // Instance graph we are using and maintaining. - hw::InstanceGraph *instanceGraph = nullptr; -}; - -} // end anonymous namespace - -void SVExtractTestCodeImplPass::runOnOperation() { - this->instanceGraph = &getAnalysis(); - - auto top = getOperation(); - - // It takes extra effort to inline modules which contains inner symbols - // referred through hierpaths or unknown operations since we have to update - // inner refs users globally. However we do want to inline modules which - // contain bound instances so create a set of inner refs used by non bind op - // in order to allow bind ops. - DenseSet innerRefUsedByNonBindOp; - top.walk([&](Operation *op) { - if (!isa(op)) - for (auto attr : op->getAttrs()) - attr.getValue().walk([&](hw::InnerRefAttr attr) { - innerRefUsedByNonBindOp.insert(attr); - }); - }); - - auto *topLevelModule = top.getBody(); - auto assertDir = - top->getAttrOfType("firrtl.extract.assert"); - auto assumeDir = - top->getAttrOfType("firrtl.extract.assume"); - auto coverDir = - top->getAttrOfType("firrtl.extract.cover"); - auto assertBindFile = - top->getAttrOfType("firrtl.extract.assert.bindfile"); - auto assumeBindFile = - top->getAttrOfType("firrtl.extract.assume.bindfile"); - auto coverBindFile = - top->getAttrOfType("firrtl.extract.cover.bindfile"); - - hw::HWSymbolCache symCache; - symCache.addDefinitions(top); - symCache.freeze(); - - auto isAssert = [&symCache](Operation *op) -> bool { - return isAssertOp(symCache, op); - }; - - auto isAssume = [&symCache](Operation *op) -> bool { - return isAssumeOp(symCache, op); - }; - - auto isCover = [&symCache](Operation *op) -> bool { - return isCoverOp(symCache, op); - }; - - // Collect modules that are already bound and add the bound instance(s) to the - // bind table, so they can be updated if the instance(s) live inside a module - // that gets inlined later. - BindTable bindTable; - addExistingBinds(topLevelModule, bindTable); - - for (auto &op : llvm::make_early_inc_range(topLevelModule->getOperations())) { - if (auto rtlmod = dyn_cast(op)) { - // Extract two sets of ops to different modules. This will add modules, - // but not affect modules in the symbol table. If any instance of the - // module is bound, then extraction is skipped. This avoids problems - // where certain simulators dislike having binds that target bound - // modules. - if (isBound(rtlmod, *instanceGraph)) - continue; - - // In the module is in test harness, we don't have to extract from it. - if (rtlmod->hasAttr("firrtl.extract.do_not_extract")) { - rtlmod->removeAttr("firrtl.extract.do_not_extract"); - continue; - } - - // Get a set for operations in the design. We can extract operations that - // don't belong to the design. - auto opsInDesign = getBackwardSlice( - rtlmod, - /*rootFn=*/ - [&](Operation *op) { - return isInDesign(symCache, op, disableInstanceExtraction, - disableRegisterExtraction); - }, - /*filterFn=*/{}); - - SmallPtrSet opsToErase; - bool anyThingExtracted = false; - anyThingExtracted |= - doModule(rtlmod, isAssert, "_assert", assertDir, assertBindFile, - bindTable, opsToErase, opsInDesign); - anyThingExtracted |= - doModule(rtlmod, isAssume, "_assume", assumeDir, assumeBindFile, - bindTable, opsToErase, opsInDesign); - anyThingExtracted |= - doModule(rtlmod, isCover, "_cover", coverDir, coverBindFile, - bindTable, opsToErase, opsInDesign); - - // If nothing is extracted and the module has an output, we are done. - if (!anyThingExtracted && rtlmod.getNumOutputPorts() != 0) - continue; - - // Here, erase extracted operations as well as dead operations. - // `opsToErase` includes extracted operations but doesn't contain all - // dead operations. Even though it's not ideal to perform non-trivial DCE - // here but we have to delete dead operations that might be an user of an - // extracted operation. - auto opsAlive = getBackwardSlice( - rtlmod, - /*rootFn=*/ - [&](Operation *op) { - // Don't remove instances not to eliminate extracted instances - // introduced above. However we do want to erase old instances in - // the original module extracted into verification parts so identify - // such instances by querying to `opsToErase`. - return isInDesign(symCache, op, - /*disableInstanceExtraction=*/true, - disableRegisterExtraction) && - !opsToErase.contains(op); - }, - /*filterFn=*/{}); - - // Walk the module and add dead operations to `opsToErase`. - op.walk([&](Operation *operation) { - // Skip the module itself. - if (&op == operation) - return; - - // Update `opsToErase`. - if (opsAlive.count(operation)) - opsToErase.erase(operation); - else - opsToErase.insert(operation); - }); - - // Inline any modules that only have inputs for test code. - if (!disableModuleInlining) - inlineInputOnly(rtlmod, *instanceGraph, bindTable, opsToErase, - innerRefUsedByNonBindOp); - - numOpsErased += opsToErase.size(); - while (!opsToErase.empty()) { - Operation *op = *opsToErase.begin(); - op->walk([&](Operation *erasedOp) { opsToErase.erase(erasedOp); }); - op->dropAllUses(); - op->erase(); - } - } - } - - // We have to wait until all the instances are processed to clean up the - // annotations. - for (auto &op : topLevelModule->getOperations()) - if (isa(op)) { - op.removeAttr("firrtl.extract.assert.extra"); - op.removeAttr("firrtl.extract.cover.extra"); - op.removeAttr("firrtl.extract.assume.extra"); - } - - markAnalysesPreserved(); -} - -std::unique_ptr -circt::sv::createSVExtractTestCodePass(bool disableInstanceExtraction, - bool disableRegisterExtraction, - bool disableModuleInlining) { - return std::make_unique(disableInstanceExtraction, - disableRegisterExtraction, - disableModuleInlining); -} diff --git a/lib/Firtool/Firtool.cpp b/lib/Firtool/Firtool.cpp index a073e6c325d6..6970771d578e 100644 --- a/lib/Firtool/Firtool.cpp +++ b/lib/Firtool/Firtool.cpp @@ -320,12 +320,6 @@ LogicalResult firtool::populateHWToSV(mlir::PassManager &pm, pm.addPass( verif::createLowerSymbolicValuesPass({opt.getSymbolicValueLowering()})); - if (opt.shouldExtractTestCode()) - pm.addPass(sv::createSVExtractTestCodePass( - opt.shouldEtcDisableInstanceExtraction(), - opt.shouldEtcDisableRegisterExtraction(), - opt.shouldEtcDisableModuleInlining())); - pm.addPass(seq::createExternalizeClockGatePass(opt.getClockGateOptions())); pm.addPass(circt::createLowerSimToSVPass()); pm.addPass(circt::createLowerSeqToSVPass( @@ -619,10 +613,6 @@ struct FirtoolCmdOptions { "repl-seq-mem-file", llvm::cl::desc("File name for seq mem metadata"), llvm::cl::init("")}; - llvm::cl::opt extractTestCode{ - "extract-test-code", llvm::cl::desc("Run the extract test code pass"), - llvm::cl::init(false)}; - llvm::cl::opt ignoreReadEnableMem{ "ignore-read-enable-mem", llvm::cl::desc("Ignore the read enable signal, instead of " @@ -804,9 +794,9 @@ circt::firtool::FirtoolOptions::FirtoolOptions() companionMode(firrtl::CompanionMode::Bind), disableAggressiveMergeConnections(false), lowerMemories(false), blackBoxRootPath(""), replSeqMem(false), replSeqMemFile(""), - extractTestCode(false), ignoreReadEnableMem(false), - disableRandom(RandomKind::None), outputAnnotationFilename(""), - enableAnnotationWarning(false), addMuxPragmas(false), + ignoreReadEnableMem(false), disableRandom(RandomKind::None), + outputAnnotationFilename(""), enableAnnotationWarning(false), + addMuxPragmas(false), verificationFlavor(firrtl::VerificationFlavor::None), emitSeparateAlwaysBlocks(false), etcDisableInstanceExtraction(false), etcDisableRegisterExtraction(false), etcDisableModuleInlining(false), @@ -844,7 +834,6 @@ circt::firtool::FirtoolOptions::FirtoolOptions() blackBoxRootPath = clOptions->blackBoxRootPath; replSeqMem = clOptions->replSeqMem; replSeqMemFile = clOptions->replSeqMemFile; - extractTestCode = clOptions->extractTestCode; ignoreReadEnableMem = clOptions->ignoreReadEnableMem; disableRandom = clOptions->disableRandom; outputAnnotationFilename = clOptions->outputAnnotationFilename; diff --git a/test/Dialect/SV/hw-extract-test-code-error.mlir b/test/Dialect/SV/hw-extract-test-code-error.mlir deleted file mode 100644 index 3f64c5c1c894..000000000000 --- a/test/Dialect/SV/hw-extract-test-code-error.mlir +++ /dev/null @@ -1,9 +0,0 @@ -// RUN: circt-opt --sv-extract-test-code %s -verify-diagnostics -module attributes {firrtl.extract.assert = #hw.output_file<"dir3/", excludeFromFileList, includeReplicatedOps>, firrtl.extract.assume.bindfile = #hw.output_file<"file4", excludeFromFileList>} { - hw.module.extern @foo_cover(in %a : i1, out b : i1) attributes {"firrtl.extract.cover.extra"} - hw.module @extract_return(in %clock: i1, out c : i1) { -// expected-error @+1 {{Extracting op with result}} - %b = hw.instance "bar_cover" @foo_cover(a: %clock : i1) -> (b : i1) - hw.output %b : i1 - } -} diff --git a/test/Dialect/SV/hw-extract-test-code.mlir b/test/Dialect/SV/hw-extract-test-code.mlir deleted file mode 100644 index 93367b901b2d..000000000000 --- a/test/Dialect/SV/hw-extract-test-code.mlir +++ /dev/null @@ -1,629 +0,0 @@ -// RUN: circt-opt --sv-extract-test-code --split-input-file %s | FileCheck %s -// CHECK-LABEL: module attributes {firrtl.extract.assert = #hw.output_file<"dir3{{/|\\\\}}" -// CHECK-NEXT: sv.macro.decl @SYNTHESIS -// CHECK-NEXT: emit.fragment @some_fragment { -// CHECK-NEXT: sv.verbatim "foo" -// CHECK-NEXT: } -// CHECK-NEXT: hw.module.extern @foo_cover -// CHECK-NOT: attributes -// CHECK-NEXT: hw.module.extern @foo_assume -// CHECK-NOT: attributes -// CHECK-NEXT: hw.module.extern @foo_assert -// CHECK-NOT: attributes -// CHECK: hw.module private @issue1246_assert(in %clock : i1) attributes {comment = "VCS coverage exclude_file", emit.fragments = [@some_fragment], output_file = #hw.output_file<"dir3{{/|\\\\}}", excludeFromFileList, includeReplicatedOps>} -// CHECK: sv.assert -// CHECK: sv.error "Assertion failed" -// CHECK: sv.error "assert:" -// CHECK: sv.error "assertNotX:" -// CHECK: sv.error "check [verif-library-assert] is included" -// CHECK: sv.func.call.procedural @fn -// CHECK: sv.fwrite -// CHECK: sv.fflush -// CHECK: sv.fatal 1 -// CHECK: foo_assert -// CHECK: hw.module private @issue1246_assume(in %clock : i1) attributes { -// CHECK-SAME: comment = "VCS coverage exclude_file" -// CHECK-SAME: emit.fragments = [@some_fragment] -// CHECK-SAME: } -// CHECK: sv.assume -// CHECK: foo_assume -// CHECK: hw.module private @issue1246_cover(in %clock : i1) attributes { -// CHECK-SAME: comment = "VCS coverage exclude_file" -// CHECK-SAME: emit.fragments = [@some_fragment] -// CHECK-SAME: } -// CHECK: sv.cover -// CHECK: foo_cover -// CHECK: hw.module @issue1246 -// CHECK-NOT: sv.assert -// CHECK-NOT: sv.assume -// CHECK-NOT: sv.cover -// CHECK-NOT: foo_assert -// CHECK-NOT: foo_assume -// CHECK-NOT: foo_cover -// CHECK: sv.bind <@issue1246::@__ETC_issue1246_assert> -// CHECK: sv.bind <@issue1246::@__ETC_issue1246_assume> {output_file = #hw.output_file<"file4", excludeFromFileList>} -// CHECK: sv.bind <@issue1246::@__ETC_issue1246_cover> -module attributes {firrtl.extract.assert = #hw.output_file<"dir3/", excludeFromFileList, includeReplicatedOps>, firrtl.extract.assume.bindfile = #hw.output_file<"file4", excludeFromFileList>} { - sv.macro.decl @SYNTHESIS - emit.fragment @some_fragment { - sv.verbatim "foo" - } - hw.module.extern @foo_cover(in %a : i1) attributes {"firrtl.extract.cover.extra"} - hw.module.extern @foo_assume(in %a : i1) attributes {"firrtl.extract.assume.extra"} - hw.module.extern @foo_assert(in %a : i1) attributes {"firrtl.extract.assert.extra"} - sv.func private @fn(out out : i32 {sv.func.explicitly_returned}) - - hw.module @issue1246(in %clock: i1) attributes {emit.fragments = [@some_fragment]} { - sv.always posedge %clock { - sv.ifdef.procedural @SYNTHESIS { - } else { - sv.if %2937 { - sv.assert %clock, immediate - sv.error "Assertion failed" - sv.error "assert:" - sv.error "assertNotX:" - sv.error "check [verif-library-assert] is included" - %out = sv.func.call.procedural @fn () : () -> (i32) - sv.fwrite %out, "foo" - sv.fflush fd %out - sv.fatal 1 - sv.assume %clock, immediate - sv.cover %clock, immediate - } - } - } - %2937 = hw.constant 0 : i1 - hw.instance "bar_cover" @foo_cover(a: %clock : i1) -> () - hw.instance "bar_assume" @foo_assume(a: %clock : i1) -> () - hw.instance "bar_assert" @foo_assert(a: %clock : i1) -> () - hw.output - } -} - -// ----- - -// Check that a module that is already going to be extracted does not have its -// asserts also extracted. This avoids a problem where certain simulators do -// not like to bind instances into bound instances. See: -// - https://github.com/llvm/circt/issues/2910 -// -// CHECK-LABEL: @AlreadyExtracted -// CHECK-COUNT-1: doNotPrint -// CHECK-NOT: doNotPrint -module attributes {firrtl.extract.assert = #hw.output_file<"dir3/", excludeFromFileList, includeReplicatedOps>} { - hw.module @AlreadyExtracted(in %clock: i1) { - sv.always posedge %clock { - sv.assert %clock, immediate - } - } - hw.module @Top(in %clock: i1) { - hw.instance "submodule" @AlreadyExtracted(clock: %clock: i1) -> () {doNotPrint} - } -} - -// ----- - -// Check that we don't extract assertions from a module with "firrtl.extract.do_not_extract" attribute. -// -// CHECK-NOT: hw.module private @ModuleInTestHarness_assert -// CHECK-NOT: firrtl.extract.do_not_extract -module attributes {firrtl.extract.assert = #hw.output_file<"dir3/", excludeFromFileList, includeReplicatedOps>} { - hw.module @ModuleInTestHarness(in %clock: i1) attributes {"firrtl.extract.do_not_extract"} { - sv.always posedge %clock { - sv.assert %clock, immediate - } - } -} - -// ----- -// Check extracted modules and their instantiations use same name - -// CHECK-LABEL: @InstanceName( -// CHECK: hw.instance "[[name:.+]]_assert" sym @{{[^ ]+}} @[[name]]_assert -// CHECK-NEXT: hw.instance "[[name:.+]]_assume" sym @{{[^ ]+}} @[[name]]_assume -// CHECK-NEXT: hw.instance "[[name:.+]]_cover" sym @{{[^ ]+}} @[[name]]_cover -module attributes {firrtl.extract.assert = #hw.output_file<"dir3/", excludeFromFileList, includeReplicatedOps>} { - hw.module @InstanceName(in %clock: i1, in %cond: i1, in %cond2: i1) { - sv.always posedge %clock { - sv.assert %cond, immediate - sv.assume %cond, immediate - sv.cover %cond, immediate - } - } -} - - -// ----- -// Check wires are extracted once - -// CHECK-LABEL: @MultiRead( -// CHECK: hw.instance "[[name:.+]]_cover" sym @{{[^ ]+}} @[[name]]_cover(foo: %0: i1, clock: %clock: i1) -module attributes {firrtl.extract.assert = #hw.output_file<"dir3/", excludeFromFileList, includeReplicatedOps>} { - hw.module @MultiRead(in %clock: i1, in %cond: i1) { - %foo = sv.wire : !hw.inout - sv.assign %foo, %cond : i1 - %cond1 = sv.read_inout %foo : !hw.inout - %cond2 = sv.read_inout %foo : !hw.inout - %cond3 = sv.read_inout %foo : !hw.inout - sv.always posedge %clock { - sv.cover %cond1, immediate - sv.cover %cond2, immediate - sv.cover %cond3, immediate - } - } -} - -// ----- -// Check extracted module ports take name of instance result when needed. - -// CHECK-LABEL: @InstResult( -// CHECK: hw.instance "[[name:.+]]_cover" sym @{{[^ ]+}} @[[name]]_cover(mem.result_name: %{{[^ ]+}}: i1, mem.1: %{{[^ ]+}}: i1, clock: %clock: i1) -module attributes {firrtl.extract.assert = #hw.output_file<"dir3/", excludeFromFileList, includeReplicatedOps>} { - hw.module @Mem(out result_name: i1, out "": i1) { - %reg = sv.reg : !hw.inout - %0 = sv.read_inout %reg : !hw.inout - hw.output %0, %0 : i1, i1 - } - // Dummy is needed to prevent the instance itself being extracted - hw.module @Dummy(in %in1: i1, in %in2: i1) {} - hw.module @InstResult(in %clock: i1) { - %0, %1 = hw.instance "mem" @Mem() -> (result_name: i1, "": i1) - hw.instance "dummy" sym @keep @Dummy(in1: %0 : i1, in2: %1 : i1) -> () - %2 = comb.and bin %0, %1 : i1 - sv.always posedge %clock { - sv.cover %2, immediate - } - } -} - -// ----- -// Check "empty" modules are inlined - -// CHECK-NOT: @InputOnly( -// CHECK-DAG: @InputOnly_assert( -// CHECK-DAG: @InputOnly_cover( -// CHECK-DAG: @InputOnlySym_cover( -// CHECK-LABEL: @InputOnlySym( -// CHECK: hw.instance "{{[^ ]+}}" sym @[[input_only_sym_cover:[^ ]+]] @InputOnlySym_cover -// CHECK-LABEL: @Top -// CHECK-NOT: hw.instance {{.+}} @Top{{.*}} -// CHECK: hw.instance "{{[^ ]+}}" sym @[[input_only_assert:[^ ]+]] @InputOnly_assert -// CHECK: hw.instance "{{[^ ]+}}" sym @[[input_only_cover:[^ ]+]] @InputOnly_cover -// CHECK: hw.instance "{{[^ ]+}}" {{.+}} @InputOnlySym -// CHECK-NOT: %0 = comb.and %1 -// CHECK-NOT: %1 = comb.and %0 -// CHECK: hw.instance "{{[^ ]+}}" {{.+}} @InputOnlyCycle_cover -// CHECK: hw.instance {{.*}} sym @[[already_bound:[^ ]+]] @AlreadyBound -// CHECK-NOT: sv.bind <@InputOnly:: -// CHECK-DAG: sv.bind <@Top::@[[input_only_assert]]> -// CHECK-DAG: sv.bind <@Top::@[[input_only_cover]]> -// CHECK-DAG: sv.bind <@InputOnlySym::@[[input_only_sym_cover]]> -// CHECK-DAG: sv.bind <@Top::@[[already_bound]]> -module { - hw.module private @AlreadyBound() {} - - hw.module private @InputOnly(in %clock: i1, in %cond: i1) { - sv.always posedge %clock { - sv.cover %cond, immediate - sv.assert %cond, immediate - } - } - - hw.module private @InputOnlySym(in %clock: i1, in %cond: i1) { - sv.always posedge %clock { - sv.cover %cond, immediate - } - } - - hw.module private @InputOnlyCycle(in %clock: i1, in %cond: i1) { - // Arbitrary code that won't be extracted, should be dead in the input only module. - // Make sure to delete them. - %0 = comb.and %1 : i1 - %1 = comb.and %0 : i1 - - sv.always posedge %clock { - sv.cover %cond, immediate - } - } - - hw.module private @InputOnlyBind(in %clock: i1, in %cond: i1) { - hw.instance "already_bound" sym @already_bound @AlreadyBound() -> () {doNotPrint} - sv.always posedge %clock { - sv.cover %cond, immediate - sv.assert %cond, immediate - } - } - - hw.module @Top(in %clock: i1, in %cond: i1, out foo: i1) { - hw.instance "input_only" @InputOnly(clock: %clock: i1, cond: %cond: i1) -> () - hw.instance "input_only_sym" sym @foo @InputOnlySym(clock: %clock: i1, cond: %cond: i1) -> () - hw.instance "input_only_cycle" @InputOnlyCycle(clock: %clock: i1, cond: %cond: i1) -> () - hw.instance "input_only_bind" @InputOnlyBind(clock: %clock: i1, cond: %cond: i1) -> () - hw.output %cond : i1 - } - - sv.bind <@InputOnlyBind::@already_bound> -} - -// ----- -// Check instance extraction - -// In AllExtracted, instances foo, bar, and baz should be extracted. -// CHECK-LABEL: @AllExtracted_cover -// CHECK: hw.instance "foo" -// CHECK: hw.instance "bar" -// CHECK: hw.instance "baz" - -// In SomeExtracted, only instance baz should be extracted. -// Check that a dead external module bozo and its operand are still alive. -// CHECK-LABEL: @SomeExtracted_cover -// CHECK-NOT: hw.instance "foo" -// CHECK-NOT: hw.instance "bar" -// CHECK-NOT: hw.instance "bozo" -// CHECK: hw.instance "baz" -// CHECK-LABEL: @SomeExtracted -// CHECK: comb.and -// CHECK: hw.instance "bozo" - -// In CycleExtracted, instance foo should be extracted despite combinational cycle. -// CHECK-LABEL: @CycleExtracted_cover -// CHECK: hw.instance "foo" - -// In ChildShouldInline, instance child should be inlined while it's instance foo is still extracted. -// CHECK-NOT: hw.module @ShouldBeInlined( -// CHECK-LABEL: @ShouldBeInlined_cover -// CHECK: hw.instance "foo" -// CHECK-LABEL: @ChildShouldInline -// CHECK-NOT: hw.instance "child" -// CHECK: hw.instance {{.+}} @ShouldBeInlined_cover - -// In ChildShouldInline2, instance bozo should not be inlined, since it was also extracted. -// CHECK-LABEL: hw.module @ChildShouldInline2 -// CHECK-NOT: hw.instance "bozo" - -// In MultiResultExtracted, instance qux should be extracted without leaving null operands to the extracted instance -// CHECK-LABEL: @MultiResultExtracted_cover -// CHECK: hw.instance "qux" -// CHECK-LABEL: @MultiResultExtracted -// CHECK-SAME: (in %[[clock:.+]] : i1, in %[[in:.+]] : i1) -// CHECK: hw.instance {{.+}} @MultiResultExtracted_cover([[in]]: %[[in]]: i1, [[clock]]: %[[clock]]: i1) - -// In SymNotExtracted, instance foo should not be extracted because it has a sym. -// CHECK-LABEL: @SymNotExtracted_cover -// CHECK-NOT: hw.instance "foo" -// CHECK-LABEL: @SymNotExtracted -// CHECK: hw.instance "foo" - -// In NoExtraInput, instance foo should be extracted, and no extra input should be added for %0 -// CHECK-LABEL: @NoExtraInput_cover -// CHECK: %[[or0:.+]] = comb.or -// CHECK: hw.instance "foo" @Foo(a: %[[or0]]: i1) -// CHECK-LABEL: @NoExtraInput -// CHECK-NOT: %{{.+}} = comb.or - -// In InstancesWithCycles, the only_testcode instances should be extracted, but the non_testcode instances should not -// CHECK-LABEL: @InstancesWithCycles_cover -// CHECK: hw.instance "only_testcode_and_instance0" -// CHECK: hw.instance "only_testcode_and_instance1" -// CHECK-LABEL: @InstancesWithCycles -// CHECK-NOT: hw.instance "only_testcode_and_instance0" -// CHECK-NOT: hw.instance "only_testcode_and_instance1" -// CHECK: hw.instance "non_testcode_and_instance0" -// CHECK: hw.instance "non_testcode_and_instance1" - -module { - sv.macro.decl @SYNTHESIS - - hw.module private @Foo(in %a: i1, out b: i1) { - hw.output %a : i1 - } - - hw.module.extern private @Bar(in %a: i1, out b: i1) - - hw.module.extern private @Baz(in %a: i1, out b: i1) - - hw.module.extern private @Qux(in %a: i1, out b: i1, out c: i1) - - hw.module.extern private @Bozo(in %a: i1, out b: i1) - - hw.module @AllExtracted(in %clock: i1, in %in: i1) { - %foo.b = hw.instance "foo" @Foo(a: %in: i1) -> (b: i1) - %bar.b = hw.instance "bar" @Bar(a: %in: i1) -> (b: i1) - %baz.b = hw.instance "baz" @Baz(a: %in: i1) -> (b: i1) - sv.always posedge %clock { - sv.if %foo.b { - sv.if %bar.b { - sv.cover %foo.b, immediate - } - } - sv.cover %bar.b, immediate - sv.cover %baz.b, immediate - } - } - - hw.module @SomeExtracted(in %clock: i1, in %in: i1, out out0: i1, out out1: i1) { - %foo.b = hw.instance "foo" @Foo(a: %in: i1) -> (b: i1) - %bar.b = hw.instance "bar" @Bar(a: %in: i1) -> (b: i1) - %baz.b = hw.instance "baz" @Baz(a: %in: i1) -> (b: i1) - %and = comb.and %in, %clock: i1 - %bozo = hw.instance "bozo" @Bozo(a: %and: i1) -> (b: i1) - sv.always posedge %clock { - sv.cover %foo.b, immediate - sv.cover %bar.b, immediate - sv.cover %baz.b, immediate - sv.cover %and, immediate - } - hw.output %foo.b, %bar.b : i1, i1 - } - - hw.module @CycleExtracted(in %clock: i1, in %in: i1) { - %foo.b = hw.instance "foo" @Foo(a: %in: i1) -> (b: i1) - %0 = comb.or %0, %foo.b : i1 - sv.always posedge %clock { - sv.cover %0, immediate - } - } - - hw.module private @ShouldBeInlined(in %clock: i1, in %in: i1) { - %foo.b = hw.instance "foo" @Foo(a: %in: i1) -> (b: i1) - sv.always posedge %clock { - sv.cover %foo.b, immediate - } - } - - hw.module private @ShouldBeInlined2(in %clock: i1, in %in: i1) { - %bozo.b = hw.instance "bozo" @Bozo(a: %in: i1) -> (b: i1) - sv.ifdef @SYNTHESIS { - } else { - sv.always posedge %clock { - sv.if %bozo.b { - sv.cover %bozo.b, immediate - } - } - } - } - - hw.module @ChildShouldInline(in %clock: i1, in %in: i1) { - hw.instance "child" @ShouldBeInlined(clock: %clock: i1, in: %in: i1) -> () - } - - hw.module @ChildShouldInline2(in %clock: i1, in %in: i1) { - hw.instance "child" @ShouldBeInlined2(clock: %clock: i1, in: %in: i1) -> () - } - - hw.module @MultiResultExtracted(in %clock: i1, in %in: i1) { - %qux.b, %qux.c = hw.instance "qux" @Qux(a: %in: i1) -> (b: i1, c: i1) - sv.always posedge %clock { - sv.cover %qux.b, immediate - sv.cover %qux.c, immediate - } - } - - hw.module @SymNotExtracted(in %clock: i1, in %in: i1) { - %foo.b = hw.instance "foo" sym @foo @Foo(a: %in: i1) -> (b: i1) - sv.always posedge %clock { - sv.cover %foo.b, immediate - } - } - - hw.module @NoExtraInput(in %clock: i1, in %in: i1) { - %0 = comb.or %in, %in : i1 - %foo.b = hw.instance "foo" @Foo(a: %0: i1) -> (b: i1) - sv.always posedge %clock { - sv.cover %0, immediate - sv.cover %foo.b, immediate - } - } - - hw.module private @Passthrough(in %in: i1, out out: i1) { - hw.output %in : i1 - } - - hw.module @InstancesWithCycles(in %clock: i1, in %in: i1, out out: i1) { - %0 = hw.instance "non_testcode_and_instance0" @Passthrough(in: %1: i1) -> (out: i1) - %1 = hw.instance "non_testcode_and_instance1" @Passthrough(in: %0: i1) -> (out: i1) - - %2 = hw.instance "only_testcode_and_instance0" @Passthrough(in: %3: i1) -> (out: i1) - %3 = hw.instance "only_testcode_and_instance1" @Passthrough(in: %2: i1) -> (out: i1) - %4 = comb.or %2, %3 : i1 - - sv.always posedge %clock { - sv.cover %1, immediate - sv.cover %2, immediate - sv.cover %4, immediate - } - - hw.output %0 : i1 - } -} - -// ----- -// Check register extraction - -module { - // CHECK-LABEL: @RegExtracted_cover - // CHECK-SAME: %designAndTestCode - // CHECK: %testCode1 = seq.firreg - // CHECK: %testCode2 = seq.firreg - // CHECK-NOT: seq.firreg - - // CHECK-LABEL: @RegExtracted - // CHECK: %symbol = seq.firreg - // CHECK: %designAndTestCode = seq.firreg - // CHECK-NOT: seq.firreg - hw.module @RegExtracted(in %clock: !seq.clock, in %reset: i1, in %in: i1, out out: i1) { - %muxed = comb.mux bin %reset, %in, %testCode1 : i1 - %testCode1 = seq.firreg %muxed clock %clock : i1 - %testCode2 = seq.firreg %testCode1 clock %clock : i1 - %symbol = seq.firreg %in clock %clock sym @foo : i1 - %designAndTestCode = seq.firreg %in clock %clock : i1 - %deadReg = seq.firreg %testCode1 clock %clock : i1 - - %clk = seq.from_clock %clock - sv.always posedge %clk { - sv.cover %testCode1, immediate - sv.cover %testCode2, immediate - sv.cover %designAndTestCode, immediate - } - - hw.output %designAndTestCode : i1 - } -} - -// ----- -// Check that constants are cloned freely. - -module { - // CHECK-LABEL: @ConstantCloned_cover(in %in : i1, in %clock : i1) - // CHECK-NEXT: %true = hw.constant true - // CHECK-NEXT: comb.xor bin %in, %true : i1 - hw.module @ConstantCloned(in %clock: i1, in %in: i1, out out: i1) { - %true = hw.constant true - %not = comb.xor bin %in, %true : i1 - - sv.always posedge %clock { - sv.cover %not, immediate - } - - hw.output %true : i1 - } -} - -// ----- -// Check that input only modules are inlined properly. - -module { - // @ShouldNotBeInlined cannot be inlined because there is a wire with an inner sym - // that is referred by hierpath op. - hw.hierpath private @Foo [@ShouldNotBeInlined::@foo] - hw.module private @ShouldNotBeInlined(in %clock: i1, in %a: i1) { - %w = sv.wire sym @foo: !hw.inout - sv.always posedge %clock { - sv.if %a { - sv.assert %a, immediate message "foo" - } - } - hw.output - } - hw.module private @Assert(in %clock: i1, in %a: i1) { - sv.always posedge %clock { - sv.if %a { - sv.assert %a, immediate message "foo" - } - } - hw.output - } - - // CHECK-LABEL: hw.module private @AssertWrapper(in %clock : i1, in %a : i1, out b : i1) { - // CHECK-NEXT: hw.instance "Assert_assert" sym @__ETC_Assert_assert @Assert_assert - // CHECK-SAME: doNotPrint - hw.module private @AssertWrapper(in %clock: i1, in %a: i1, out b: i1) { - hw.instance "a3" @Assert(clock: %clock: i1, a: %a: i1) -> () - hw.output %a: i1 - } - - // CHECK-NOT: @InputOnly - hw.module private @InputOnly(in %clock: i1, in %a: i1) { - hw.instance "a4" @Assert(clock: %clock: i1, a: %a: i1) -> () - } - - // CHECK-LABEL: hw.module @Top(in %clock : i1, in %a : i1, in %b : i1) { - // CHECK-NEXT: hw.instance "Assert_assert" sym @__ETC_Assert_assert_0 @Assert_assert - // CHECK-SAME: doNotPrint - // CHECK-NEXT: hw.instance "Assert_assert" sym @__ETC_Assert_assert @Assert_assert - // CHECK-SAME: doNotPrint - // CHECK-NEXT: hw.instance "Assert_assert" sym @__ETC_Assert_assert_1 @Assert_assert - // CHECK-SAME: doNotPrint - // CHECK-NEXT: hw.instance "should_not_be_inlined" @ShouldNotBeInlined - // CHECK-NOT: doNotPrint - hw.module @Top(in %clock: i1, in %a: i1, in %b: i1) { - hw.instance "a1" @Assert(clock: %clock: i1, a: %a: i1) -> () - hw.instance "a2" @Assert(clock: %clock: i1, a: %b: i1) -> () - hw.instance "a3" @InputOnly(clock: %clock: i1, a: %b: i1) -> () - hw.instance "should_not_be_inlined" @ShouldNotBeInlined (clock: %clock: i1, a: %b: i1) -> () - hw.output - } - // CHECK: sv.bind <@Top::@__ETC_Assert_assert> - // CHECK-NEXT: sv.bind <@Top::@__ETC_Assert_assert_0> - // CHECK-NEXT: sv.bind <@Top::@__ETC_Assert_assert_1> - // CHECK-NEXT: sv.bind <@AssertWrapper::@__ETC_Assert_assert> -} - - -// ----- - -// Check that the port order is [%clock, %in] and not the reverse. This is -// trying to guarantee taht the generated port order is stable with additions or -// deletions of internal operations. The critical thing in this test is the -// existence of the `%0 = seq.from_clock %clock` operation. This operation was -// added and it caused the port order to become [%in, %clock]. This shouldn't -// happen. -// -// See: https://github.com/llvm/circt/issues/6072 - -module { - // CHECK-LABEL: hw.module @PortOrder_6072 - hw.module @PortOrder_6072(in %clock : !seq.clock, in %in : i1) { - hw.instance "dut" @Foo(clock: %clock: !seq.clock, in: %in: i1) -> () - hw.output - } - // CHECK: hw.module private @Foo_cover(in %clock : !seq.clock, in %in : i1) - hw.module private @Foo(in %clock: !seq.clock, in %in: i1) { - %0 = seq.from_clock %clock - sv.cover.concurrent posedge %0, %in label "cover__hello" - hw.output - } -} - - -// ----- - -// Check that no anonymous ports are created and all the port names are unique. - -module { - hw.module @PortName(in %clock : !seq.clock, in %in : i1) { - %x = hw.instance "pF" @PortNameFoo(clock: %clock: !seq.clock, "": %in: i1) -> (o: i1) - hw.output - } - // CHECK-LABEL: hw.module private @PortNameFoo_cover - // CHECK-SAME: (in %clock : !seq.clock, in %port_1 : i1, in %port_2 : i1) - hw.module private @PortNameFoo(in %clock: !seq.clock, in %1: i1, out o : i1) { - // CHECK: hw.instance "PortNameFoo_cover" - // CHECK-SAME: @PortNameFoo_cover(clock: %clock: !seq.clock, port_1: %0: i1, port_2: %1: i1) -> () - %0 = seq.from_clock %clock - %2 = comb.xor %1, %1 : i1 - sv.cover.concurrent posedge %0, %1 label "cover__hello1" - sv.cover.concurrent posedge %0, %2 label "cover__hello2" - hw.output %2 : i1 - } -} - -// ----- - -// Check that verif ops are also extracted. - -// CHECK: hw.module private @VerifOps_assert -// CHECK: hw.module private @VerifOps_assume -// CHECK: hw.module private @VerifOps_cover -hw.module @VerifOps(in %a : i1, in %b : i1) { - %true = hw.constant true - verif.assert %true : i1 - verif.assume %true : i1 - verif.cover %true : i1 - hw.output -} - -// ----- - -// Check that clocked verif ops are also extracted. - -// CHECK: hw.module private @ClockedVerifOps_assert -// CHECK: hw.module private @ClockedVerifOps_assume -// CHECK: hw.module private @ClockedVerifOps_cover -hw.module @ClockedVerifOps(in %a : i1, in %en : i1, in %clock: i1) { - %true = hw.constant true - verif.clocked_assert %a if %en, posedge %clock : i1 - verif.clocked_assume %a if %en, posedge %clock : i1 - verif.clocked_cover %a if %en, posedge %clock : i1 - hw.output -} diff --git a/test/circt-synth/basic.mlir b/test/circt-synth/basic.mlir index 2a47156618e2..ed4a71a2c241 100644 --- a/test/circt-synth/basic.mlir +++ b/test/circt-synth/basic.mlir @@ -35,15 +35,6 @@ hw.module @and(in %a: i2, in %b: i2, in %c: i2, in %d: i1, out and: i2) { hw.output %0 : i2 } -// CHECK-LABEL: @verification( -// CHECK-NOT: sv.assert -hw.module @verification(in %a: i1, out result: i1) { - sv.initial { - sv.assert %a, immediate - } - hw.output %a : i1 -} - // TOP-LABEL: hw.module @unrelated // TOP-NEXT: comb.add %a, %b hw.module @unrelated(in %a: i2, in %b: i2, out and: i2) { diff --git a/test/firtool/extract-test-code.fir b/test/firtool/extract-test-code.fir deleted file mode 100644 index 8f58e8f65e8f..000000000000 --- a/test/firtool/extract-test-code.fir +++ /dev/null @@ -1,68 +0,0 @@ -; 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 - -FIRRTL version 4.0.0 -circuit Top: - module Foo: - input a : UInt<1> - output b : UInt<1> - connect 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 - connect foo.a, cond - connect b, foo.b - - assert(clock, cond, b, "Some assertion") - - connect 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> - input en : UInt<1> - assert(clock, cond, en, "Some assertion") - - ; CHECK: module Top_assert( - ; CHECK-NOT: endmodule - ; CHECK: wire _GEN = ~en; - ; CHECK: foo: assert property (disable iff (_GEN) cond); - ; CHECK: endmodule - - public module Top: - input clock : Clock - input cond : UInt<1> - input en : UInt<1> - output out : UInt<1> - - inst instance_extracted of InstanceExtracted - connect instance_extracted.clock, clock - connect instance_extracted.cond, cond - connect out, instance_extracted.out - - inst input_only of InputOnly - connect input_only.clock, clock - connect input_only.cond, cond - connect input_only.en, en - - intrinsic(circt_verif_assert, cond, en) diff --git a/test/firtool/print.fir b/test/firtool/print.fir index 9b9e3d473a05..d5cc00088d30 100644 --- a/test/firtool/print.fir +++ b/test/firtool/print.fir @@ -1,5 +1,4 @@ ; RUN: firtool %s | FileCheck %s -; RUN: firtool %s --extract-test-code | FileCheck --check-prefix=ETC %s FIRRTL version 5.1.0 ; CHECK: `ifndef SYNTHESIS @@ -23,10 +22,6 @@ FIRRTL version 5.1.0 ; CHECK-NEXT: `endif // not def SYNTHESIS circuit PrintTest: ; CHECK-LABEL: module PrintTest - ; ETC-LABEL: module PrintTest_assert - ; ETC-LABEL: PrintTest - ; ETC-NOT: $fwrite - ; ETC-NOT: $fflush public module PrintTest : input clock : Clock input cond : UInt<1> diff --git a/tools/circt-synth/circt-synth.cpp b/tools/circt-synth/circt-synth.cpp index ec343b81069a..e620e5f0bcde 100644 --- a/tools/circt-synth/circt-synth.cpp +++ b/tools/circt-synth/circt-synth.cpp @@ -183,11 +183,6 @@ static void partiallyLegalizeCombToAIG(SmallVectorImpl &ops) { // Add a default synthesis pipeline and analysis. static void populateCIRCTSynthPipeline(PassManager &pm) { - // ExtractTestCode is used to move verification code from design to - // remove registers/logic used only for verification. - pm.addPass(sv::createSVExtractTestCodePass( - /*disableInstanceExtraction=*/false, /*disableRegisterExtraction=*/false, - /*disableModuleInlining=*/false)); auto pipeline = [](OpPassManager &pm) { circt::synth::AIGLoweringPipelineOptions loweringOptions; loweringOptions.disableDatapath = disableDatapath;