Skip to content

Commit

Permalink
Fix esp8266 timer1 testing (#2709)
Browse files Browse the repository at this point in the history
This PR fixes an issue with `HostTests` which hangs consistently when testing Timer1 (used by HardwareTimer) on the Esp8266.

The problem occurs whilst checking each timer for consistency against system time.
This happens because the test code doesn't set a callback interrupt routine.
It is only a problem on the Esp8266 because the callback routine is set directly on the interrupt vector.
Other architectures use indirection and so do nothing if the callback isn't set.
NB. Doing this with the Esp8266 would increase interrupt latency which would affect PWM timing, etc.

Normal code uses the `HardwareTimer` class which contains logic to ensure this doesn't happen during setup.

Timer1 has only a 23-bit counter so fails test with /16 divisor as it wraps at around 1.7s (test duration is 2 seconds).
Fixed by restricting duration to timer maximum (less 1ms).

Elapsed tick calculation check also improved by first checking whether timer is an UP or DOWN counter,
and using timer maximum tick value to handle wraps correctly.

This PR also reduces the number of test loops on the Host (from 2000 to 50).

Note: Bug was introduced in #2456. Timer1 needed to be correctly configured (but inactive) to produce correct result for Esp32.
  • Loading branch information
mikee47 authored Jan 16, 2024
1 parent 5173129 commit 3d86fb3
Show file tree
Hide file tree
Showing 2 changed files with 29 additions and 10 deletions.
3 changes: 1 addition & 2 deletions Sming/Core/HardwareTimer.h
Original file line number Diff line number Diff line change
Expand Up @@ -141,11 +141,10 @@ class Timer1Api : public CallbackTimerApi<Timer1Api<clkdiv, mode>>
template <hw_timer_clkdiv_t clkdiv, HardwareTimerMode mode> uint8_t Timer1Api<clkdiv, mode>::state;
template <hw_timer_clkdiv_t clkdiv, HardwareTimerMode mode> uint32_t Timer1Api<clkdiv, mode>::interval;

template <hw_timer_clkdiv_t clkdiv = TIMER_CLKDIV_16, HardwareTimerMode mode = eHWT_NonMaskable>

/**
* @brief Hardware Timer class template with selectable divider and interrupt mode
*/
template <hw_timer_clkdiv_t clkdiv = TIMER_CLKDIV_16, HardwareTimerMode mode = eHWT_NonMaskable>
using HardwareTimer1 = CallbackTimer<Timer1Api<clkdiv, mode>>;

/**
Expand Down
36 changes: 28 additions & 8 deletions tests/HostTests/modules/Clocks.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,11 @@ template <class Clock, typename TimeType> class ClockTestTemplate : public TestG
{
printLimits();

for(unsigned i = 0; i < 2000; ++i) {
unsigned loopCount{2000};
#ifdef ARCH_HOST
loopCount = 50;
#endif
while(loopCount--) {
auto value = os_random();
check<NanoTime::Milliseconds>(value);
check<NanoTime::Microseconds>(value);
Expand All @@ -37,19 +41,30 @@ template <class Clock, typename TimeType> class ClockTestTemplate : public TestG

TEST_CASE("vs. system time")
{
constexpr uint32_t duration{2000000};
auto startTime = system_get_time();
// Determine whether this is an up or down-counter
auto startTicks = Clock::ticks();
os_delay_us(100);
auto endTicks = Clock::ticks();
bool isDownCounter = (endTicks < startTicks);
debug_w("%s is %s counter", Clock::typeName(), isDownCounter ? "DOWN" : "UP");

// Run for a second or two and check timer ticks correspond approximately with system clock
constexpr uint64_t maxDuration = Clock::maxTicks().template as<NanoTime::Microseconds>() - 5000ULL;
constexpr uint32_t duration = std::min(2000000ULL, maxDuration);
auto startTime = system_get_time();
startTicks = Clock::ticks();
uint32_t time;
while((time = system_get_time()) < startTime + duration) {
while((time = system_get_time()) - startTime < duration) {
//
}
auto endTicks = Clock::ticks();
// Handle both up and down counters
auto elapsedTicks = (endTicks >= startTicks) ? endTicks - startTicks : startTicks - endTicks;
endTicks = Clock::ticks();
if(isDownCounter) {
std::swap(startTicks, endTicks);
}
uint32_t elapsedTicks = (endTicks - startTicks) % (Clock::maxTicks() + 1);

debug_w("System time elapsed: %u", time - startTime);
debug_w("%s ticks: %u", Clock::typeName(), elapsedTicks);
debug_w("Ticks: %u (%u - %u)", elapsedTicks, startTicks, endTicks);
debug_w("Ratio: x %f", float(elapsedTicks) / (time - startTime));
uint32_t us = Micros::ticksToTime(elapsedTicks);
debug_w("Apparent time: %u", us);
Expand Down Expand Up @@ -252,10 +267,15 @@ template <hw_timer_clkdiv_t clkdiv>
class Timer1ClockTestTemplate : public ClockTestTemplate<Timer1Clock<clkdiv>, uint32_t>
{
public:
static void IRAM_ATTR callback(void*)
{
}

void execute() override
{
// Configure the hardware to match selected clock divider
Timer1Api<clkdiv, eHWT_Maskable> timer;
timer.setCallback(callback, nullptr);
timer.arm(false);

ClockTestTemplate<Timer1Clock<clkdiv>, uint32_t>::execute();
Expand Down

0 comments on commit 3d86fb3

Please sign in to comment.