Support a generalized freechips.rocketchip.annotations.InternalVerifBlackBoxAnnotation in ExtractTestCode (#1956)

Covert freechips.rocketchip.annotations.InternalVerifBlackBoxAnnotation annotations to a generalized set of annotations that works not just for cover, but assert and assume extraction too.  Process those annotations in ExtractTestCode.
This commit is contained in:
Andrew Lenharth 2021-10-08 15:49:24 -07:00 committed by GitHub
parent 061a0f300f
commit e6cde1f094
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 83 additions and 10 deletions

View File

@ -926,3 +926,8 @@ modules' bind file. This attribute has type OutputFileAttr.
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.

View File

@ -180,6 +180,7 @@ public:
/// removed, false otherwise.
bool removeAnnotation(Annotation anno);
bool removeAnnotation(Attribute anno);
bool removeAnnotation(StringRef className);
/// Remove all annotations from this annotation set for which `predicate`
/// returns true. The predicate is guaranteed to be called on every

View File

@ -41,9 +41,11 @@ static const char assumeAnnoClass[] =
static const char coverAnnoClass[] =
"sifive.enterprise.firrtl.ExtractCoverageAnnotation";
static const char dutAnnoClass[] = "sifive.enterprise.firrtl.MarkDUTAnnotation";
static const char verifBBClass[] =
"freechips.rocketchip.annotations.InternalVerifBlackBoxAnnotation";
/// Attribute that indicates that the module hierarchy starting at the annotated
/// module should be dumped to a file.
/// Attribute that indicates that the module hierarchy starting at the
/// annotated module should be dumped to a file.
static const char moduleHierarchyFileAttrName[] = "firrtl.moduleHierarchyFile";
/// Attribute that indicates where some json files should be dumped.
@ -872,8 +874,6 @@ FIRRTLModuleLowering::lowerExtModule(FExtModuleOp oldModule,
if (auto defName = oldModule.defname())
verilogName = defName.getValue();
loweringState.processRemainingAnnotations(oldModule,
AnnotationSet(oldModule));
// Build the new hw.module op.
auto builder = OpBuilder::atBlockEnd(topLevelModule);
auto nameAttr = builder.getStringAttr(oldModule.getName());
@ -881,8 +881,14 @@ FIRRTLModuleLowering::lowerExtModule(FExtModuleOp oldModule,
// no known default values in the extmodule. This ensures that the
// hw.instance will print all the parameters when generating verilog.
auto parameters = getHWParameters(oldModule, /*ignoreValues=*/true);
return builder.create<hw::HWModuleExternOp>(oldModule.getLoc(), nameAttr,
ports, verilogName, parameters);
auto newModule = builder.create<hw::HWModuleExternOp>(
oldModule.getLoc(), nameAttr, ports, verilogName, parameters);
// Transform module annotations
AnnotationSet annos(oldModule);
if (annos.removeAnnotation(verifBBClass))
newModule->setAttr("firrtl.extract.cover.extra", builder.getUnitAttr());
loweringState.processRemainingAnnotations(oldModule, annos);
return newModule;
}
/// Run on each firrtl.module, transforming it from an firrtl.module into an
@ -903,9 +909,12 @@ FIRRTLModuleLowering::lowerModule(FModuleOp oldModule, Block *topLevelModule,
builder.create<hw::HWModuleOp>(oldModule.getLoc(), nameAttr, ports);
if (auto outputFile = oldModule->getAttr("output_file"))
newModule->setAttr("output_file", outputFile);
// Transform module annotations
AnnotationSet annos(oldModule);
// Mark the design under test as a module of interest for exporting module
// hierarchy information.
if (AnnotationSet::removeAnnotations(oldModule, dutAnnoClass))
if (annos.removeAnnotation(dutAnnoClass))
newModule->setAttr(
moduleHierarchyFileAttrName,
hw::OutputFileAttr::getFromDirectoryAndFilename(
@ -913,8 +922,9 @@ FIRRTLModuleLowering::lowerModule(FModuleOp oldModule, Block *topLevelModule,
getMetadataDir(oldModule->getParentOfType<CircuitOp>()).getValue(),
"module_hier.json",
/*excludeFromFileList=*/true));
loweringState.processRemainingAnnotations(oldModule,
AnnotationSet(oldModule));
if (annos.removeAnnotation(verifBBClass))
newModule->setAttr("firrtl.extract.cover.extra", builder.getUnitAttr());
loweringState.processRemainingAnnotations(oldModule, annos);
return newModule;
}

View File

@ -272,6 +272,13 @@ bool AnnotationSet::removeAnnotation(Attribute anno) {
[&](Annotation other) { return other.getDict() == anno; });
}
/// Remove an annotation from this annotation set. Returns true if any were
/// removed, false otherwise.
bool AnnotationSet::removeAnnotation(StringRef className) {
return removeAnnotations(
[&](Annotation other) { return other.getClass() == className; });
}
/// Remove all annotations from this annotation set for which `predicate`
/// returns true.
bool AnnotationSet::removeAnnotations(

View File

@ -312,13 +312,25 @@ void SVExtractTestCodeImplPass::runOnOperation() {
top->getAttrOfType<hw::OutputFileAttr>("firrtl.extract.cover.bindfile");
auto isAssert = [](Operation *op) -> bool {
if (auto inst = dyn_cast<hw::InstanceOp>(op))
if (auto mod = inst.getReferencedModule())
if (mod->getAttr("firrtl.extract.assert.extra"))
return true;
return isa<AssertOp>(op) || isa<FinishOp>(op) || isa<FWriteOp>(op) ||
isa<AssertConcurrentOp>(op);
};
auto isAssume = [](Operation *op) -> bool {
if (auto inst = dyn_cast<hw::InstanceOp>(op))
if (auto mod = inst.getReferencedModule())
if (mod->getAttr("firrtl.extract.assume.extra"))
return true;
return isa<AssumeOp>(op) || isa<AssumeConcurrentOp>(op);
};
auto isCover = [](Operation *op) -> bool {
if (auto inst = dyn_cast<hw::InstanceOp>(op))
if (auto mod = inst.getReferencedModule())
if (mod->getAttr("firrtl.extract.cover.extra"))
return true;
return isa<CoverOp>(op) || isa<CoverConcurrentOp>(op);
};
@ -331,6 +343,14 @@ void SVExtractTestCodeImplPass::runOnOperation() {
doModule(rtlmod, isCover, "_cover", coverDir, coverBindFile);
}
}
// We have to wait until all the instances are processed to clean up the
// annotations.
for (auto &op : topLevelModule->getOperations())
if (isa<hw::HWModuleOp, hw::HWModuleExternOp>(op)) {
op.removeAttr("firrtl.extract.assert.extra");
op.removeAttr("firrtl.extract.cover.extra");
op.removeAttr("firrtl.extract.assume.extra");
}
}
std::unique_ptr<Pass> circt::sv::createSVExtractTestCodePass() {

View File

@ -1074,4 +1074,6 @@ firrtl.circuit "Simple" attributes {annotations = [{class =
%b = firrtl.bitcast %a : (!firrtl.bundle<valid: uint<2>, ready: uint<1>, data: uint<3>>) -> (!firrtl.vector<uint<2>, 3>)
// CHECK: hw.bitcast %0 : (!hw.struct<valid: i2, ready: i1, data: i3>) -> !hw.array<3xi2>
}
firrtl.extmodule @chkcoverAnno(in %clock: !firrtl.clock) attributes {annotations = [{class = "freechips.rocketchip.annotations.InternalVerifBlackBoxAnnotation"}]}
}

View File

@ -1,12 +1,36 @@
// RUN: circt-opt --sv-extract-test-code %s | FileCheck %s
// CHECK-LABEL: hw.module @issue1246_assert(%clock: i1) attributes {output_file = #hw.output_file<"dir3/", excludeFromFileList, includeReplicatedOps>}
// CHECK-LABEL: module attributes {firrtl.extract.assert = #hw.output_file<"dir3/"
// 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 @issue1246_assert(%clock: i1) attributes {output_file = #hw.output_file<"dir3/", excludeFromFileList, includeReplicatedOps>}
// CHECK: sv.assert
// CHECK: foo_assert
// CHECK: hw.module @issue1246_assume(%clock: i1)
// CHECK-NOT: attributes
// CHECK: sv.assume
// CHECK: foo_assume
// CHECK: hw.module @issue1246_cover(%clock: i1)
// CHECK-NOT: attributes
// 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 @__ETC_issue1246_assert in @issue1246
// CHECK: sv.bind @__ETC_issue1246_assume in @issue1246 {output_file = #hw.output_file<"file4", excludeFromFileList>}
// CHECK: sv.bind @__ETC_issue1246_cover in @issue1246
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(%a : i1) attributes {"firrtl.extract.cover.extra"}
hw.module.extern @foo_assume(%a : i1) attributes {"firrtl.extract.assume.extra"}
hw.module.extern @foo_assert(%a : i1) attributes {"firrtl.extract.assert.extra"}
hw.module @issue1246(%clock: i1) -> () {
sv.always posedge %clock {
sv.ifdef.procedural "SYNTHESIS" {
@ -14,10 +38,14 @@ module attributes {firrtl.extract.assert = #hw.output_file<"dir3/", excludeFrom
sv.if %2937 {
sv.assert %clock : i1
sv.assume %clock : i1
sv.cover %clock : i1
}
}
}
%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
}
}