Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Verif] Add formal test intent ops #7145

Merged
merged 17 commits into from
Jul 17, 2024
81 changes: 81 additions & 0 deletions include/circt/Dialect/Verif/VerifOps.td
Original file line number Diff line number Diff line change
Expand Up @@ -212,4 +212,85 @@ def YieldOp : VerifOp<"yield", [
];
}

//===----------------------------------------------------------------------===//
// Verification intent ops
//===----------------------------------------------------------------------===//

/// BMC -- short for bounded model check.
def BMC : I32EnumAttrCase<"BMC", 0, "bmc">;
dobios marked this conversation as resolved.
Show resolved Hide resolved
/// KIP -- short for k-Inductive-Proof.
dobios marked this conversation as resolved.
Show resolved Hide resolved
def KIP : I32EnumAttrCase<"KIP", 0, "induction">;
dobios marked this conversation as resolved.
Show resolved Hide resolved

dobios marked this conversation as resolved.
Show resolved Hide resolved
def FormalTestTypeAttr : I32EnumAttr<"FormalTestType", "formal test type",
dobios marked this conversation as resolved.
Show resolved Hide resolved
[BMC, KIP]> {
let cppNamespace = "circt::verif";
}

def FormalOp : VerifOp<"formal"> {
let summary = "defines a formal verification test";
let description = [{
This operation defines a formal verification test with potentially additional
assertions and assumptions. This should be written by declaring symbolic
dobios marked this conversation as resolved.
Show resolved Hide resolved
values that are then connected to a hardware instance. These symbols can then
be reasoned about in additional assertions. The region will then be converted
into btor2, SMTLib, or SystemVerilog and ignored during regular SystemVerilog
dobios marked this conversation as resolved.
Show resolved Hide resolved
emission.

Additional information about how the test should be conducted can be passed
using the two supported attributes. These attributes are `k`, which defines
the bounds of the test, and `type` which defines the type of formal test
being done, i.e. Bounded Model Checking (BMC) or k-Induction.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: happy to be overruled on grounds of repetition but it might be nice to say the syntax here, just makes the docs slightly easier to digest

}];

let arguments = (ins OptionalAttr<APIntAttr>:$k,
OptionalAttr<FormalTestTypeAttr>:$test_type,
OptionalAttr<InnerSymAttr>:$inner_sym);
maerhart marked this conversation as resolved.
Show resolved Hide resolved

let regions = (region SizedRegion<1>:$test_region);

let assemblyFormat = [{
attr-dict (`sym` $inner_sym^)? `(` (`k` `:` $k^)? (`type` `:` $test_type^)? `)`
$test_region
}];
}

//===----------------------------------------------------------------------===//
// Formal Verification Ops
//===----------------------------------------------------------------------===//

def SymbolicValOp : VerifOp<"symbolic_val", [Pure]> {
dobios marked this conversation as resolved.
Show resolved Hide resolved
let summary = "declare a sybolic value for formal verification";
dobios marked this conversation as resolved.
Show resolved Hide resolved
let description = [{
This operation declares a symbolic value that can then be used to reason
about hw.instance inputs in a symbolic manner in future assertions and
dobios marked this conversation as resolved.
Show resolved Hide resolved
assumptions. The type result must be explicitly marked.
dobios marked this conversation as resolved.
Show resolved Hide resolved
}];
let results = (outs AnyType:$result);
dobios marked this conversation as resolved.
Show resolved Hide resolved

let assemblyFormat = "attr-dict `:` type($result)";

let hasVerifier = 1;
}

def ConcreteValOp : VerifOp<"concrete_val", [
Pure, AllTypesMatch<["init", "update", "result"]>
]> {
let summary = "declare a concrete value for formatl verification";
dobios marked this conversation as resolved.
Show resolved Hide resolved
let description = [{
This opertaion declares a concrete value that can then be used in the
dobios marked this conversation as resolved.
Show resolved Hide resolved
reasoning surrounding symbolic values, allowing for a form of concolic
reasoning to take place in a verif.formal block. Concrete values are
dobios marked this conversation as resolved.
Show resolved Hide resolved
defined by an initial value and an update function.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we specify when this update function is executed? Does this op effectively behave like a register with an implicit clock?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I see this as the op is executed on every "time-step" so it would be converted into a register with an implicit clock yes. I'm open to other ideas if you have any

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In its current state one could argue that it's not really necessary because you could just use the regular register operation, but I think it's still useful to have it so we still know that it is an input and not just part of the design. Also, providing the clock for the regular register would probably require a block argument from verif.formal. So, I'd leave it as is. I just think, it'd just be nice to describe this implicit clock and how the value updating works a bit more in the description. Basically, verif.formal provides an implicit base clock and the symbolic and concrete operations provide values for all inputs (including the actual clocks) based on this implicit clock.

}];

let arguments = (ins AnyType:$init, AnyType:$update);
dobios marked this conversation as resolved.
Show resolved Hide resolved
dobios marked this conversation as resolved.
Show resolved Hide resolved
let results = (outs AnyType:$result);

let assemblyFormat = [{
$init `,` $update attr-dict `:` type($result)
}];

let hasVerifier = 1;
}

#endif // CIRCT_DIALECT_VERIF_VERIFOPS_TD
20 changes: 20 additions & 0 deletions lib/Dialect/Verif/VerifOps.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,26 @@ LogicalResult LogicEquivalenceCheckingOp::verifyRegions() {
return success();
}

//===----------------------------------------------------------------------===//
// Formal Ops
//===----------------------------------------------------------------------===//

LogicalResult SymbolicValOp::verify() {
if (!isa<FormalOp>(getParentOp()))
return emitOpError()
<< "Symbolic values can only be used in a formal test!";
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There is a HasParent constraint defined in tablegen that you could use to auto-generate this.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh that's neat! I didn't know about that, thanks!


return success();
}

LogicalResult ConcreteValOp::verify() {
if (!isa<FormalOp>(getParentOp()))
return emitOpError()
<< "Concrete values can only be used in a formal test!";

return success();
}

//===----------------------------------------------------------------------===//
// Generated code
//===----------------------------------------------------------------------===//
Expand Down
Loading