-
Notifications
You must be signed in to change notification settings - Fork 647
[svsim] Fix bugs in generated simulation code for Verilator backend. #5054
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
Conversation
Rewrite the run_simulation function in simulation_driver.cpp to handle delayed events in BlackBox modules properly. Add a test checking in ChiSimSpec.scala to demonstrate the case.
seldridge
left a comment
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.
Generally LGTM. Thanks for the PR!
Nitty comments throughout. I am running this on our internal CI as I want to make sure it doesn't break anything. We (obviously) aren't exercising this code path as I've never seen this error come up before. I do want to make sure there are no regressions, though.
| new svsim.verilator.Backend.CompilationSettings( | ||
| traceStyle = conf.traceStyle, | ||
| outputSplit = conf.outputSplit, | ||
| outputSplitCFuncs = conf.outputSplitCFuncs, | ||
| disabledWarnings = conf.disabledWarnings, | ||
| disableFatalExitOnWarnings = true, | ||
| enableAllAssertions = conf.enableAllAssertions, | ||
| timing = Some(svsim.verilator.Backend.CompilationSettings.Timing.TimingEnabled) | ||
| ) |
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 constructor is now deprecated to avoid problems with binary compatibility and changes to this case class. The way to do this now is:
| new svsim.verilator.Backend.CompilationSettings( | |
| traceStyle = conf.traceStyle, | |
| outputSplit = conf.outputSplit, | |
| outputSplitCFuncs = conf.outputSplitCFuncs, | |
| disabledWarnings = conf.disabledWarnings, | |
| disableFatalExitOnWarnings = true, | |
| enableAllAssertions = conf.enableAllAssertions, | |
| timing = Some(svsim.verilator.Backend.CompilationSettings.Timing.TimingEnabled) | |
| ) | |
| svsim.verilator.Backend.CompilationSettings.default | |
| .withDisableFatalExitOnWarnings(true) | |
| .withTiming(Some(svsim.verilator.Backend.CompilationSettings.Timing.TimingEnabled)) |
| uint64_t stepDuration = | ||
| std::min(static_cast<uint64_t>(delay), testbench->nextTimeSlot() - context->time()); |
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 static_cast from signed to unsigned looks dangerous. Can the PR include an assert that delay is positive? I'm also slightly nervous that delay could go negative inside this while loop. It may make sense to check it every iteration. WDYT?
Unfortunately, Verilog DPI doesn't have an unsigned type that I am aware of, so the signature of run_simulation can't be changed.
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.
Actually this while loop is executed only when delay > 0. So if the delay becomes negative it will automatically break the loop.
But I think your worry is valid. So I'll add an assertion at the beginning of the function(to make sure the input delay is non-negative), and one after the loop exits(to check whether delay goes negative after the while loop). This can guarantee that delay won't turn negative throughout the function. Do you think this is reasonable ?
| if (*finish) { | ||
| return; | ||
| } |
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:
| if (*finish) { | |
| return; | |
| } | |
| if (*finish) | |
| return; |
| if (delay > 0) { | ||
| context->timeInc(delay); | ||
| } |
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:
| if (delay > 0) { | |
| context->timeInc(delay); | |
| } | |
| if (delay > 0) | |
| context->timeInc(delay); |
| it("should dump a VCD waveform when traceStyle is Vcd and enableWaves is used") { | ||
|
|
||
| implicit val vaerilator = verilatorWithVcd | ||
| implicit val verilator = verilatorWithVcd |
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 fixing this!
|
|
||
| // Some simple logic using the clock | ||
| val counter = RegInit(0.U(8.W)) | ||
| counter := counter + 1.U |
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:
| counter := counter + 1.U | |
| counter :<= counter + 1.U |
| if (stepDuration <= 0) { | ||
| failWithError("Found event that should be sheduled ealier."); | ||
| } |
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 throughout: no need for braces around single statements following if statements:
| if (stepDuration <= 0) { | |
| failWithError("Found event that should be sheduled ealier."); | |
| } | |
| if (stepDuration <= 0) | |
| failWithError("Found event that should be sheduled ealier."); |
| testbench->eval(); | ||
| *finish = context->gotFinish(); | ||
| if (*finish) { | ||
| return; | ||
| } |
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 code snippet appears twice. Consider moving this to a static function/lambda.
| if (delay > 0) { | ||
| context->timeInc(delay); | ||
| } | ||
| } |
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 think this makes sense. I was trying to see if there was some way to combine this with the earlier testbench-eval() to make this a do-while loop. However, there is always going to need to be some optional execution after stepDuration is subtracted.
| dut.io.delayedInitial.expect(0.U) | ||
| dut.clock.step(1, 1000) | ||
|
|
||
| dut.io.delayedInitial.expect(1.U) | ||
| dut.clock.step(1, 1000) | ||
|
|
||
| dut.io.in.poke(0.U) |
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.
Please include .expect of delayedIn as well.
|
@seldridge Thank you for your detailed comments and suggestions. This is actually my first pr to an open-source project so I learned a lot from these comments. |
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.
LGTM
Edit: I did not see any internal failures with this change.
| delayed.io.in :<= io.in | ||
| io.delayedIn :<= delayed.io.delayedIn | ||
| io.delayedInitial :<= delayed.io.delayedInitial |
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 explicit assignment is fine. You may consider:
| delayed.io.in :<= io.in | |
| io.delayedIn :<= delayed.io.delayedIn | |
| io.delayedInitial :<= delayed.io.delayedInitial | |
| delayed.io :<>= io |
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 tried delayed.io :<>= io but it doesn't compile on my machine. The error is:
[411] [error] src/test/scala-2/chiselTests/simulator/scalatest/ChiselSimSpec.scala 499:20: Connection between sink (Delayed.delayedInitial: IO[UInt<1>]) and source (Foo.io.delayedInitial: IO[UInt<1>]) failed @: delayedInitial in Delayed cannot be written from module Foo.
[411] [error] delayed.io :<>= io
[411] [error] ^
[411] [error] src/test/scala-2/chiselTests/simulator/scalatest/ChiselSimSpec.scala 499:20: Connection between sink (Delayed.delayedIn: IO[UInt<1>]) and source (Foo.io.delayedIn: IO[UInt<1>]) failed @: delayedIn in Delayed cannot be written from module Foo.
[411] [error] delayed.io :<>= io
[411] [error] ^
[411] [error] src/test/scala-2/chiselTests/simulator/scalatest/ChiselSimSpec.scala 499:20: Connection between sink (Foo.io.in: IO[UInt<1>]) and source (Delayed.in: IO[UInt<1>]) failed @: io.in in Foo cannot be written from module Delayed.
[411] [error] delayed.io :<>= io
[411] [error] ^
[411] [error] There were 3 error(s) during hardware elaboration.
I'm not sure whether it's my environment's problem or there's something wrong with the expression.
Edit: I found the fix. Revert the expression to io :<>= delayed.io and it works.
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 may be that they need to be reversed. However, I've had the same issues with these operations and just wind up writing it manually. The PR is fine. No changes needed.
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.
Just now I've found the correct way. Using io :<>= delayed.io and it works.
|
Let me know when you're ready with this and I can land it. |
|
Fixed and ready to go. Thanks for your review ! |
Rewrite the
run_simulation functioninsimulation_driver.cppto handle delayed events in BlackBox modules properly. Nowrun_simulationwill check whether there're pending delayed events, and wake up these pending events by calling theevalfunction at correct timepoint to allow the delay scheduler schedule these events.Also add a test in
ChiSimSpec.scalato demonstrate the case.Contributor Checklist
docs/src?Type of Improvement
Desired Merge Strategy
Release Notes
Fix the
run_simulationAPI in resource filesimulation-driver.cppfor verilator backend to allow delayed events fromBlackBoxbe scheduled by Verilator properly.Reviewer Checklist (only modified by reviewer)
3.6.x,5.x, or6.xdepending on impact, API modification or big change:7.0)?Enable auto-merge (squash)and clean up the commit message.Create a merge commit.