-
Notifications
You must be signed in to change notification settings - Fork 636
Add FormalTest marker #4635
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
Add FormalTest marker #4635
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The general boilerplate of adding a new construct to emit in FIRRTL looks good to me. Just a higher-level question about the user API.
} else { | ||
module._proposedName | ||
} | ||
private[chisel3] override def generateComponent(): Option[Component] = None |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If this is directly adding components to the builder in the constructor, and doesn't actually generateComponent as a BaseModule, maybe this should be more of a helper function rather than a BaseModule class that is instantiated? It seems like all we really need here is a public API to push a DefFormalTest, which can pass the name and parameters to FIRRTL. So maybe this could be more like:
object FormalTest {
def apply(...) = {
...
}
}
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hmmm yeah, that is a good point. I'll give that a shot!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for the suggestion! That cleaned things up quite a bit 🙂. Not entirely sure where to get the source location from now, and the FIRRTL-side Component needs a def id: BaseModule
field, but I can make both work by just using the test harness module for that. 👍
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Cool, that does look cleaner to me. If you want to materialize a SourceInfo at the callsite, I think you can add an implicit argument to your apply
method, like:
def apply(...)(implicit sourceInfo: SourceInfo)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah nice, I was wondering if that would just work, or if the plugin was hardwired to only touch certain constructs. I've added that and threaded it through to the FIRRTL emission. Works like a charm! Thanks for the pointer 🙂
d735f39
to
d5f11cc
Compare
40c7cc5
to
83f8031
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is looking good to me. I think this is a reasonable API, but curious what others think.
} | ||
|
||
generateFirrtlAndFileCheck(new Foo)( | ||
"""|CHECK-LABEL: formal Foo of Foo_2 : |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: maybe add a check for module Foo_2
so we know that is indeed the module name? Could maybe use the capture / substitution functionality as well, so we could do something like CHECK: module [[NAME:Foo.+]]
and later CHECK: formal Foo of [[NAME]]
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah yeah, nice catch. Forgot that this actually runs through FileCheck with all the bells and whistles.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done 👍
FormalTest(this) | ||
FormalTest(this, MapTestParam(Map("hello" -> StringTestParam("world")))) | ||
FormalTest( | ||
this, | ||
MapTestParam( | ||
Map( | ||
"a_int" -> IntTestParam(42), | ||
"b_double" -> DoubleTestParam(13.37), | ||
"c_string" -> StringTestParam("hello"), | ||
"d_array" -> ArrayTestParam(Seq(IntTestParam(42), StringTestParam("hello"))), | ||
"e_map" -> MapTestParam( | ||
Map( | ||
"x" -> IntTestParam(42), | ||
"y" -> StringTestParam("hello") | ||
) | ||
) | ||
) | ||
), | ||
"thisBetterWork" | ||
) | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's fine that in this PR we're just testing for the basic FIRRTL emission, but how are Chisel users really intended to use this?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'd expect this to combine nicely with some of the work that @tmckay-sifive has been doing on defining unit tests and inline tests. There could be an API like formalTest { ... }
that you get for your Testable
or HasTest
(don't remember what the proposed trait was exactly), which would mark the generate test harness as a formal test. We may also want to allow the user to define some of the parameters on the test, or at least supplement whatever Chisel does internally with their own stuff. There might also be cases where the user really wants to manually mark modules as formal tests through this API, if they need multiple test runs on the same harness with different parameters.
TL;DR, I'd imagine something like this as a the lowest barrier of entry for formally testing modules:
class MyModule extends RawModule with Testable {
val a = IO(Input(UInt(8.W)))
val b = IO(Input(UInt(8.W)))
val z = IO(Output(UInt(8.W)))
// ... module body ...
formalTest "Complete" { dut =>
AssertProperty(dut.a + dut.b === dut.z)
}
formalTest "NeutralA" { dut =>
AssumeProperty(dut.a === 0.U)
AssertProperty(dut.b === dut.z)
}
formalTest "NeutralB" { dut =>
AssumeProperty(dut.b === 0.U)
AssertProperty(dut.a === dut.z)
}
}
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm wondering if we really want FormalTest
and the Parameters classes to be part of the Chisel public API vs. a more sugary API like your sketch above. In any case, not gonna sandbag here, we should just make sure to massage the API before we really start advertising this to users
83f8031
to
6ebe8fa
Compare
The FIRRTL spec defines a `formal` construct to indicate that a module should be executed as a formal test. Currently, there is no way to emit this construct from Chisel. This PR adds a user-facing `FormalTest` class that can be used to mark a module as to be executed as a formal test. This would commonly be used inside a test harness module to mark the surrounding module as a test. For example: class TestHarness extends RawModule { FormalTest(this) } The formal test can be given an optional name and a map of parameters. The parameters are distinct from blackbox modules in that they do not map to Verilog parameters, but instead allow for any recursive nesting of integers, doubles, strings, arrays, and maps, as prescribed by the FIRRTL spec. Multiple formal test markers may be added to a single module, which may be useful if a test should be run with different sets of user-defined parameters. This PR also adds the necessary IR nodes to the FIRRTL and Chisel FIRRTL IRs.
6ebe8fa
to
597d577
Compare
The FIRRTL spec defines a
formal
construct to indicate that a module should be executed as a formal test. Currently, there is no way to emit this construct from Chisel.This PR adds a user-facing
FormalTest
class that can be used to mark a module as to be executed as a formal test. This would commonly be used inside a test harness module to mark the surrounding module as a test. For example:The formal test can be given an optional name and a map of parameters. The parameters are distinct from blackbox modules in that they do not map to Verilog parameters, but instead allow for any recursive nesting of integers, doubles, strings, arrays, and maps, as prescribed by the FIRRTL spec. Multiple formal test markers may be added to a single module, which may be useful if a test should be run with different sets of user-defined parameters.
This PR also adds the necessary IR nodes to the FIRRTL and Chisel FIRRTL IRs.
Contributor Checklist
docs/src
?Type of Improvement
Desired Merge Strategy
Release Notes
Reviewer Checklist (only modified by reviewer)
3.6.x
,5.x
, or6.x
depending on impact, API modification or big change:7.0
)?Enable auto-merge (squash)
, clean up the commit message, and label withPlease Merge
.Create a merge commit
.