Statement/Branch coverage for all tests#263
Conversation
|
Thanks for the PR Andrew, this is great! I really like the thought you put into how to communicate the coverage results back to the user. Some notes on how this could be improved:
For high firrtl / Chisel coverage it might be interesting to consider the pass that Daniel Kasza has been working on: https://github.com/danielkasza/dank-formal/blob/main/src/main/scala/transforms.scala#L13 He is essentially adding cover statements for all branches in high firrtl. This information should be enough to calculate the line and branch coverage and you could try to map it back to the Chisel source using the |
|
Thanks a lot for the quick response! I completely agree with the comments, this solution felt a bit brute-force-y when I implemented it. Thanks again for the review! |
Thanks for the PR!
I know that writing your first firrtl pass is hard since there is very little documentation. Feel free to ask any questions that come up on the firrtl gitter: https://gitter.im/freechipsproject/firrtl |
chick
left a comment
There was a problem hiding this comment.
This looks great to me. Thanks for effort, it looks like a great contribution.
I will defer to Kevin on the technical analysis. I'd also like to maintain the scalafmt
formatting also.
New Version of the Statement Coverage PRAs per the request, the coverage parser that was used to add additional coverage ports to the source code, via string parsing, was converted into a firrtl pass. FIRRTL passA firrtl pass AddCoverageExpressions was created, which takes care of adding the additional coverage ports that were previously done using brute-force string parsing. Coverage package objectThe coverage package object defines a new Info child class that allows us to store the names of the newly added ports in the circuit's info. These names are then used when compiling the final report (which is still done by modifying the original source code's string for printing purposes). CoverageThe Coverage class, which was previously named Let me know if ever there's anything that still needs to be changed/improved ( @chick @ekiwi )! |
|
Thanks a lot for the review @ekiwi , your comments make a lot of sense, I'll get right to it! |
chick
left a comment
There was a problem hiding this comment.
This is looking pretty good. Check out my suggestions in comments for GetFirrtlAst
|
I missed one other suggestion for how to schedule and run the pass. I think you should add this to AddCoverageExpressions /** Only run this pass if there is an EnableCoverageAnnotation present
*
* @param state
* @return
*/
override def execute(state: CircuitState): CircuitState = {
if(state.annotations.contains(EnableCoverageAnnotation)) {
state.copy(circuit = run(state.circuit))
} else {
state
}
} |
|
Thanks for the comments @chick ! Does this current state look good? |
I think it is a fine implementation for the time being. Maybe we could mark it as experimental to allow for future API breaks. The next step would require us to implement support for the cover statement in treadle. This way we can replace the output ports with cover statements which would almost automatically make this work for submodules as well. @dobios work can serve as a good use-case example to see how a custom coverage report generator might want to plug into a generic cover statement implementation. I.e., the idea would be that treadle can load coverage report generator classes that analyze the coverage and turn it into a human readable report. |
Nice thanks!
I did see that since I've implemented the first version of this the cover statement was added to Chisel, it does seem like a more elegant solution. Is anyone currently working on adding it to Treadle? |
|
To backport this to 1.3.x, we should tweak the change to Basically, define the |
I don't think this needs to be backported. This is more of a technology demo and not really ready for production usage anyways. Like currently it only works if you only have a single module in your design. |
|
Makes sense, fair enough. Getting the SNAPSHOT autopublishing in will be nice so people can try it out using the SNAPSHOT. |
Adding Statement/Branch Coverage to Treadle
The idea was to modify the treadle tester so that it would make certain modifications to the given FIRRTL source code in order to track the line coverage. Once that was done, we needed to check these additional ports and compile the results in a line coverage report. The modifications related to this work can be found here. Note that coverage can be enabled using the EnableCoverage annotation.
Modifying the input FIRRTL code
The first thing that needed to be done was to add ports to the given design. This is done depending on the amount of multiplexers that are in the design, since any code that isn't in a multiplexer is executed by default. The idea would thus be to do the following:
These are all of the modifications that are needed to keep track of line coverage during a simulation.
Generating a Coverage report
The main work here was interpreting the meaning of the outputs added to the FIRRTL source. The idea here was to do a second pass of the coverage parser once a given test was done. The big difference now is that we have access to the values of the different coverage validators, which allows us to know which multiplexer paths were taken during the different tests. So we can compile the data retrieved from the coverage validators througout an entire test suite to find out which paths were taken and which were skipped. The results are then shown in a coverage report that has the following structure:
Here the first line gives a general percentage of how many of the possible control paths were taken and then the
COVERAGE REPORTshows a modified version of the FIRRTL source with coverage prefixes added to each line:+means that the line was covered by a test in the test suite.-means that the line was missed during all of the test suite's tests.Interpreting the Coverage Report
What this report tells us (in the above example) is that one of our artificially added FIRRTL lines wasn't covered. This information can be used to deduce which path of our multiplexer was taken. For example, the above
COVERAGE REPORT:actually tells us that the case where
io_a == 0was never tested and thus one of our potential outputs was never tested.Mapping loFIRRTL to Chisel
One could discuss the usefulness of having line coverage on Chisel code. One main case where this would not be as helpful would be when writing hardware generators, since there the notion of coverage isn't as clear: Has the hardware generator been tested? or more interestingly Has the generated hardware been tested?. I think that this question is open to discussion. This coverage report is thus given at the FIRRTL level but an other interesting report would contain the same line coverage information, shown directly in our original source description.
Future work on this part of the project could thus be to find a way to map the results to the original Chisel source using source locators. An other version of the coverage report would thus be: