Skip to content

Conversation

tmckay-sifive
Copy link
Contributor

Contributor Checklist

  • Did you add Scaladoc to every public function/method?
  • Did you add at least one test demonstrating the PR?
  • Did you delete any extraneous printlns/debugging code?
  • Did you specify the type of improvement?
  • Did you add appropriate documentation in docs/src?
  • Did you request a desired merge strategy?
  • Did you add text to be included in the Release Notes for this change?

Type of Improvement

  • Feature (or new API)

Desired Merge Strategy

  • Squash: The PR will be squashed and merged (choose this if you have no preference).

Release Notes

  • Add a ChiselSim API for simulating inline tests

Reviewer Checklist (only modified by reviewer)

  • Did you add the appropriate labels? (Select the most appropriate one based on the "Type of Improvement")
  • Did you mark the proper milestone (Bug fix: 3.6.x, 5.x, or 6.x depending on impact, API modification or big change: 7.0)?
  • Did you review?
  • Did you check whether all relevant Contributor checkboxes have been checked?
  • Did you do one of the following when ready to merge:
    • Squash: You/ the contributor Enable auto-merge (squash) and clean up the commit message.
    • Merge: Ensure that contributor has cleaned up their commit history, then merge with Create a merge commit.

Comment on lines -70 to +189
abstract class TestHarness[M <: RawModule, R](test: TestParameters[M, R])
extends FixedIOModule(new TestResultBundle)
abstract class TestHarness[M <: RawModule](test: TestParameters[M])
extends FixedIOModule(new TestHarnessInterface)
Copy link
Contributor Author

Choose a reason for hiding this comment

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

I removed the R type parameter (the thing returned from a test-body and consumed by the testharness).

Instead of leaving this API wide open and expecting users to implement their own test-testharness interfaces, I added a new TestConfiguration case class.

The idea is that as the test body elaborates it builds a TestConfiguration, which is more or less "how the testharness should handle this test."

Based on what that is (e.g. "run for 10 cycles", "end when this signal is asserted"), the testharness knows how to wiggle the finish/success pins.

In the future, this might also include things like "expect some assertion to fire" if we want to support tests that expect to fail.

intf.success := successCondition.getOrElse(true.B)
failureMessage.foreach { failureMessage =>
when(intf.finish && intf.success) {
printf(cf"${testName} failed: ${failureMessage}")
Copy link
Contributor

Choose a reason for hiding this comment

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

this says failed, but it prints when intf.success is true... am i missing something?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

No not missing anything. Also, I forgot I had this feature here--need to add a test for it.

Copy link
Member

@seldridge seldridge left a comment

Choose a reason for hiding this comment

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

Some comments about the stimulus.

Comment on lines 14 to 21
class InlineTestSignaledFailureException private[simulator]
extends RuntimeException(
dramaticMessage(
header = Some(s"The test finished and signaled failure"),
body = ""
)
)
with NoStackTrace
Copy link
Member

Choose a reason for hiding this comment

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

This should be moved into Simulator.scala's Exceptions object. I also think this is a generic TestFailed exception and not specific to inline unit tests.

@seldridge seldridge added the Feature New feature, will be included in release notes label Jun 5, 2025
Copy link
Member

@seldridge seldridge left a comment

Choose a reason for hiding this comment

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

This is looking fantastic. I am having a lot of fun playing with this locally.

Address the feedback and tag me for another review.

Copy link
Member

Choose a reason for hiding this comment

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

This is not for this PR, but we need to move this out of experimental.

Comment on lines -212 to +319
protected final def test[R](
protected final def test(
testName: String
)(testBody: Instance[M] => R)(implicit testHarnessGenerator: TestHarnessGenerator[M, R]): Unit = {
)(testBody: Instance[M] => TestConfiguration)(implicit testHarnessGenerator: TestHarnessGenerator[M]): Unit = {
Copy link
Member

Choose a reason for hiding this comment

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

At first glance, it seems like the test configuration is a parameter of the test method itself and not the return of the lambda. I.e., it seems more natural for this to be:

protected def test(testName: String, config: TestConfiguration)(body: Instance[R] => Unit)

However, the PR is using this to force the communication of how the success and finish are hooked up.

I don't see anything wrong here, just this is unexpected.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I agree it's a bit unexpected, but it is useful to have it as a return type so that it can reference hardware in the test body (e.g. some sentinel value to watch for finishing the test).

@tmckay-sifive tmckay-sifive requested a review from seldridge June 10, 2025 20:46
Copy link
Member

@seldridge seldridge left a comment

Choose a reason for hiding this comment

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

LGTM. Please land with the comments addressed.

The one semi-big thing which needs to get sorted out is the reporting. Though, this can happen in follow-ons. There's a pretty big difference between an assertion failing in an inline test vs. an assertion failing in a non-inline test. E.g., if a test fails an assertion inline you see:

[365] - should simulate and fail early if assertion raised *** FAILED ***
[365]   chisel3.simulator.Exceptions$TestsFailed: One or more tests failed during simulation
[365] ------------------------------------------------------------------------------
[365] ModuleWithTests tests: 0 succeeded, 1 failed (assertion)
[365] ------------------------------------------------------------------------------
[365]   ...

However, this would include much more information if not inline: what assertion failed, the line numbers, etc. The infrastructure for showing this is all there, it just needs to be plumbed through to this output.

Also, we should probably err on the side of providing too much information. E.g., just binning by assertion assert or timeout is likely not enough.

This isn't blocking and I am confident that this will get sorted out as we use it.

@tmckay-sifive tmckay-sifive merged commit f9a8602 into chipsalliance:main Jun 12, 2025
15 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Feature New feature, will be included in release notes
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants