Tips for the proper use of wait as an event-based constructs
Avoid bias to the boundary values of a range
Suppose you want a sub-scenario to be invoked when the vehicle reaches some speed within the range [30..50]kph as in the following example:
car1: vehicle
do serial():
wait (car1.state.speed in [30..50]kph)
car1.my_scenario()
Now suppose that the test starts with car1 at a speed lower than 30kph, then it accelerates. As soon as car1 reaches 30kph, the condition in the event is satisfied and my_scenario is invoked. The same is true if car1 starts with a speed above 50kph and then decelerates. In this case, my_scenario is invoked at 50kph. Consequently, in the regression, this scenario will be strongly biased to the bound values of the range.
The same effect occurs if the in constraint is replaced with an inequality constraint:
do serial():
wait (car1.state.speed > 30kph)
car1.my_scenario()
There are two ways to avoid the bias. The first way is to use a concrete generated value in a wait statement rather than a range:
car1: vehicle
required_speed: speed with:
keep(it in [30..50]kph)
do serial():
wait (car1.state.speed == required_speed)
car1.my_scenario()
In this case, the execution waits until the randomly generated value required_speed is reached by car1.
The second way is to use the speed modifier to specify a starting condition on car1 for my_scenario:
car1: vehicle
do serial():
wait (@top.clk)
car1.my_scenario() with:
car1.speed([30..50]kph, at: start)
Avoid the use of the same actor in parallel threads with wait
A common pattern that involves wait statements is illustrated in the following example:
do parallel():
cut_in()
serial():
wait (@car1_starts_changing_lane)
env.weather(rain)
There are two threads running in parallel. In the first thread, a maneuver is executed by one or more actors. In the second thread, there is a wait for some event, followed by some change. In this example, as soon as car1 starts the lane change maneuver, it starts raining.
Since events are usually unpredictable, planning with respect to events is difficult and requires very aggressive assumptions to be taken. Those assumptions may not fit with what happens in reality and can cause an incomplete scenario, for example:
car1: vehicle
car2: vehicle
do parallel():
cut_in(car1, car2)
serial():
wait(@rain_started)
car1.drive() with:
change_speed(50kph)
In this example, car1 and car2 execute a cut_in maneuver, where car2 is the cutting in vehicle. From the other side, when the rain starts (an unpredictable event), car1 is required to accelerate strongly. During planning, an assumption about the rain_started event may be taken, in which case, the cut_in maneuver is planned with respect to this assumption. However, in reality, the event may occur much earlier or much later and the sudden unplanned acceleration of car1 will prevent the cut_in from happening.
The recommendation in this case is to not use the same actor in both threads of a parallel statement where one thread is event-based. This will reduce the risk of an incomplete scenario due to the unpredictable occurrence of the event.