diff --git a/svsim/src/main/resources/simulation-driver.cpp b/svsim/src/main/resources/simulation-driver.cpp index 98e76f4454..dcbe1e1098 100644 --- a/svsim/src/main/resources/simulation-driver.cpp +++ b/svsim/src/main/resources/simulation-driver.cpp @@ -71,7 +71,7 @@ void initTestBenchScope() { testbenchScope = svGetScope(); } #ifdef __cplusplus extern "C" { #endif -extern void run_simulation(int timesteps); +extern int run_simulation(int timesteps); extern void simulation_main(int argc, const char **argv); #ifdef __cplusplus } @@ -672,7 +672,8 @@ static void processCommand() { if (*lineCursor != '\n') { failWithError("Unexpected data at end of RUN command."); } - run_simulation(time); + if (run_simulation(time)) + receivedDone = true; sendAck(); break; @@ -757,9 +758,15 @@ static void processCommand() { } (*tickingPort.setter)(inPhaseValue); - run_simulation(timestepsPerPhase); + if (run_simulation(timestepsPerPhase)) { + receivedDone = true; + break; + } (*tickingPort.setter)(outOfPhaseValue); - run_simulation(timestepsPerPhase); + if (run_simulation(timestepsPerPhase)) { + receivedDone = true; + break; + } } cycles--; // Consume the unbalanced increment from the while condition @@ -926,16 +933,18 @@ void simulation_main(int argc, char const **argv) { delete context; } -void run_simulation(int delay) { +int run_simulation(int delay) { if(!delay) { testbench->eval_step(); - return; + return context->gotFinish(); } testbench->eval(); + if (context->gotFinish()) + return context->gotFinish(); context->timeInc(delay); + return 0; } } // extern "C" #endif // SVSIM_ENABLE_VERILATOR_SUPPORT - diff --git a/svsim/src/main/scala/Workspace.scala b/svsim/src/main/scala/Workspace.scala index 09e09d9b4c..091bfb5780 100644 --- a/svsim/src/main/scala/Workspace.scala +++ b/svsim/src/main/scala/Workspace.scala @@ -150,10 +150,12 @@ final class Workspace( l(" export \"DPI-C\" task run_simulation;") l(" task run_simulation;") l(" input int timesteps;") + l(" output int done;") l(" #timesteps;") + l(" done = 0;") l(" endtask") l(" `else") - l(" import \"DPI-C\" function void run_simulation(int timesteps);") + l(" import \"DPI-C\" function int run_simulation(int timesteps);") l(" `endif") l() diff --git a/svsim/src/test/resources/Finish.sv b/svsim/src/test/resources/Finish.sv new file mode 100644 index 0000000000..1af580af6b --- /dev/null +++ b/svsim/src/test/resources/Finish.sv @@ -0,0 +1,6 @@ +module Finish(input clock); + + always @ (posedge clock) + $finish; + +endmodule diff --git a/svsim/src/test/scala/BackendSpec.scala b/svsim/src/test/scala/BackendSpec.scala index 3dce2d9407..7b23bb573c 100644 --- a/svsim/src/test/scala/BackendSpec.scala +++ b/svsim/src/test/scala/BackendSpec.scala @@ -298,6 +298,39 @@ trait BackendSpec extends AnyFunSpec with Matchers { } } } + + it("ends the simulation on '$finish' (#4700)") { + workspace.reset() + workspace.elaborateFinishTest() + workspace.generateAdditionalSources() + simulation = workspace.compile( + backend + )( + workingDirectoryTag = name, + commonSettings = CommonCompilationSettings(), + backendSpecificSettings = compilationSettings, + customSimulationWorkingDirectory = None, + verbose = false + ) + simulation.run( + verbose = false, + executionScriptLimit = None + ) { controller => + val clock = controller.port("clock") + clock.tick( + inPhaseValue = 0, + outOfPhaseValue = 1, + timestepsPerPhase = 1, + maxCycles = 8, + sentinel = None + ) + } + val re = ".*Verilog \\$finish.*".r + new BufferedReader(new FileReader(s"${simulation.workingDirectoryPath}/simulation-log.txt")).lines + .filter(re.matches(_)) + .toArray + .size must be(1) + } } } } diff --git a/svsim/src/test/scala/Resources.scala b/svsim/src/test/scala/Resources.scala index c4ad8e9719..d9febdb12a 100644 --- a/svsim/src/test/scala/Resources.scala +++ b/svsim/src/test/scala/Resources.scala @@ -131,5 +131,20 @@ object Resources { ) ) } + def elaborateFinishTest(): Unit = { + workspace.addPrimarySourceFromResource(getClass, "/Finish.sv") + workspace.elaborate( + ModuleInfo( + name = "Finish", + ports = Seq( + new ModuleInfo.Port( + name = "clock", + isSettable = true, + isGettable = false + ) + ) + ) + ) + } } }