mirror of
https://gitlab.rtems.org/rtems/rtos/rtems.git
synced 2026-09-28 15:53:06 +08:00
libtest: Sweep the time point when search stalls
T_interrupt_test() bisects a busy wait to place an interrupt inside the action of a test. It adjusts the bracket on an early and on a late interrupt, but T_INTERRUPT_TEST_CONTINUE fell through both cases: the bounds stayed as they were, the sample index did not advance, and the next iteration used the very same busy count. A test whose interrupt reliably lands inside the action, yet which is not satisfied by that particular time point, therefore repeated one time point until it ran out of iterations. A continue says the interrupt hit the action but reports nothing about early or late, so there is no gradient to bisect on. Count them, and once the search has produced nothing else for a while, step the time point through the bracket instead of using its middle. A stepped time point which leaves the action yields an early or a late result again, which clears the counter and lets the bisection narrow the bracket around the action. A search which does make progress never reaches the counter and keeps the behaviour it had. The bracket is covered in a fixed number of steps rather than in steps of one busy count. This is not needed to make the search converge, it keeps the sweep predictable: the step follows the width of the bracket, a bracket narrower than the step count degrades to a step of one, and there is no division by a width which may be zero. The last point needs the lower bound to stay below the upper bound. The bisection did not guarantee this, since a busy count taken from the bracket of all samples is accounted to one sample only. Pull the opposite bound along in this case. spintrcritical20 on the erc32 BSP hit exactly this. It reported T_INTERRUPT_TEST_TIMEOUT with 9997 of its 10000 iterations landing inside the action and none of them satisfying the test. It now completes. All twenty spintrcritical tests pass on erc32, leon2, gr712rc, gr740 and gr740 in SMP configuration. The simulator is deterministic, so repeating a test under it adds no information. A host simulation of the search over randomised action windows, satisfied only after three distinct outcomes, completes in 200 of 200 runs with the sweep and in 1 of 200 without it. Assisted-by: Claude:claude-opus-5 claude-code Signed-off-by: Sebastian Huber <sebastian.huber@embedded-brains.de>
This commit is contained in:
committed by
Joel Sherrill
parent
3c8572d74f
commit
827861d9c1
@@ -10,7 +10,7 @@
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (C) 2020 embedded brains GmbH & Co. KG
|
||||
* Copyright (C) 2020, 2026 embedded brains GmbH & Co. KG
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
@@ -56,6 +56,17 @@ typedef T_interrupt_test_state ( *T_interrupt_test_handler )( void * );
|
||||
|
||||
#define T_INTERRUPT_SAMPLE_COUNT 8
|
||||
|
||||
/*
|
||||
* Number of consecutive continue results after which the search is considered
|
||||
* to be stalled on one time point.
|
||||
*/
|
||||
#define T_INTERRUPT_STALL_COUNT 16
|
||||
|
||||
/*
|
||||
* Number of time points visited by one sweep through the bracket.
|
||||
*/
|
||||
#define T_INTERRUPT_SWEEP_STEPS 64
|
||||
|
||||
typedef struct {
|
||||
uint_fast32_t one_tick_busy;
|
||||
int64_t t0;
|
||||
@@ -370,6 +381,7 @@ T_interrupt_test_state T_interrupt_test(
|
||||
int32_t ns_per_tick;
|
||||
size_t sample;
|
||||
uint32_t iter;
|
||||
uint_fast32_t stall;
|
||||
|
||||
ctx = T_interrupt_setup( config, arg );
|
||||
T_push_fixture( &ctx->node, &T_interrupt_fixture );
|
||||
@@ -383,6 +395,7 @@ T_interrupt_test_state T_interrupt_test(
|
||||
}
|
||||
|
||||
sample = 0;
|
||||
stall = 0;
|
||||
|
||||
for ( iter = 0; iter < config->max_iteration_count; ++iter ) {
|
||||
T_interrupt_test_state state;
|
||||
@@ -403,6 +416,36 @@ T_interrupt_test_state T_interrupt_test(
|
||||
*/
|
||||
busy = ( lower_sum + upper_sum ) / ( 2 * T_INTERRUPT_SAMPLE_COUNT );
|
||||
|
||||
/*
|
||||
* A continue tells us that the interrupt did hit the action, but that
|
||||
* the test was not satisfied by this time point. It says nothing about
|
||||
* the interrupt being early or late, so the bisection gets no gradient
|
||||
* from it. A run of them means the search is stalled on one time point
|
||||
* and would stay there for every remaining iteration. Only then step
|
||||
* the time point through the bracket, so that a search which does make
|
||||
* progress is left alone. A stepped time point which leaves the action
|
||||
* yields an early or a late result again, which lets the bisection
|
||||
* narrow the bracket around the action.
|
||||
*
|
||||
* The bracket is covered in a fixed number of steps, so that the step
|
||||
* follows the width of the bracket and a bracket narrower than the step
|
||||
* count degrades to a step of one.
|
||||
*/
|
||||
if ( stall >= T_INTERRUPT_STALL_COUNT ) {
|
||||
uint_fast32_t sweep;
|
||||
uint_fast32_t step;
|
||||
|
||||
sweep = ( stall - T_INTERRUPT_STALL_COUNT ) % T_INTERRUPT_SWEEP_STEPS;
|
||||
step = ( upper_sum - lower_sum ) /
|
||||
( T_INTERRUPT_SAMPLE_COUNT * T_INTERRUPT_SWEEP_STEPS );
|
||||
|
||||
if ( step == 0 ) {
|
||||
step = 1;
|
||||
}
|
||||
|
||||
busy = lower_sum / T_INTERRUPT_SAMPLE_COUNT + sweep * step;
|
||||
}
|
||||
|
||||
t = sbttons( _Timecounter_Sbinuptime() );
|
||||
d = ( t - ctx->t0 ) % ns_per_tick;
|
||||
t += ns_per_tick / 4 - d;
|
||||
@@ -451,12 +494,24 @@ T_interrupt_test_state T_interrupt_test(
|
||||
if ( state == T_INTERRUPT_TEST_EARLY ) {
|
||||
uint_fast32_t lower;
|
||||
|
||||
stall = 0;
|
||||
upper_sum -= upper_bound[ sample ];
|
||||
upper_sum += busy;
|
||||
upper_bound[ sample ] = busy;
|
||||
|
||||
/* Round down to make sure no underflow happens */
|
||||
/*
|
||||
* A stepped busy count comes from the bracket of all samples and may lie
|
||||
* below the lower bound of this sample. Pull the lower bound along, so
|
||||
* that it stays below the upper bound.
|
||||
*/
|
||||
lower = lower_bound[ sample ];
|
||||
|
||||
if ( lower > busy ) {
|
||||
lower_sum -= lower - busy;
|
||||
lower = busy;
|
||||
}
|
||||
|
||||
/* Round down to make sure no underflow happens */
|
||||
delta = lower / 32;
|
||||
lower_sum -= delta;
|
||||
lower_bound[ sample ] = lower - delta;
|
||||
@@ -473,21 +528,35 @@ T_interrupt_test_state T_interrupt_test(
|
||||
* interrupt would be late.
|
||||
*/
|
||||
|
||||
stall = 0;
|
||||
lower_sum -= lower_bound[ sample ];
|
||||
lower_sum += busy;
|
||||
lower_bound[ sample ] = busy;
|
||||
|
||||
/*
|
||||
* A stepped busy count comes from the bracket of all samples and may lie
|
||||
* above the upper bound of this sample. Pull the upper bound along, so
|
||||
* that it stays above the lower bound.
|
||||
*/
|
||||
upper = upper_bound[ sample ];
|
||||
|
||||
if ( upper < busy ) {
|
||||
upper_sum += busy - upper;
|
||||
upper = busy;
|
||||
}
|
||||
|
||||
/*
|
||||
* The one tick busy count value is not really
|
||||
* trustable on some platforms. Allow the upper bound
|
||||
* to grow over this value in time.
|
||||
*/
|
||||
upper = upper_bound[ sample ];
|
||||
delta = ( upper + 31 ) / 32;
|
||||
upper_sum += delta;
|
||||
upper_bound[ sample ] = upper + delta;
|
||||
|
||||
sample = ( sample + 1 ) % T_INTERRUPT_SAMPLE_COUNT;
|
||||
} else if ( state == T_INTERRUPT_TEST_CONTINUE ) {
|
||||
++stall;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user