230 lines
8.6 KiB
C++
230 lines
8.6 KiB
C++
// Copyright 2020 The Pigweed Authors
|
|
//
|
|
// Licensed under the Apache License, Version 2.0 (the "License"); you may not
|
|
// use this file except in compliance with the License. You may obtain a copy of
|
|
// the License at
|
|
//
|
|
// https://www.apache.org/licenses/LICENSE-2.0
|
|
//
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
|
// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
|
// License for the specific language governing permissions and limitations under
|
|
// the License.
|
|
#pragma once
|
|
|
|
#include <stddef.h>
|
|
#include <stdint.h>
|
|
|
|
#include "pw_preprocessor/util.h"
|
|
|
|
// The backend implements this header to provide the following SystemClock
|
|
// parameters, for more detail on the parameters see the SystemClock usage of
|
|
// them below:
|
|
// PW_CHRONO_SYSTEM_CLOCK_PERIOD_SECONDS_NUMERATOR
|
|
// PW_CHRONO_SYSTEM_CLOCK_PERIOD_SECONDS_DENOMINATOR
|
|
// constexpr pw::chrono::Epoch pw::chrono::backend::kSystemClockEpoch;
|
|
// constexpr bool pw::chrono::backend::kSystemClockFreeRunning;
|
|
// constexpr bool pw::chrono::backend::kSystemClockNmiSafe;
|
|
#include "pw_chrono_backend/system_clock_config.h"
|
|
|
|
#ifdef __cplusplus
|
|
|
|
#include <chrono>
|
|
#include <ratio>
|
|
|
|
namespace pw::chrono {
|
|
namespace backend {
|
|
|
|
// The ARM AEBI does not permit the opaque 'time_point' to be passed via
|
|
// registers, ergo the underlying fundamental type is forward declared.
|
|
// A SystemCLock tick has the units of one SystemClock::period duration.
|
|
// This must be thread and IRQ safe and provided by the backend.
|
|
int64_t GetSystemClockTickCount();
|
|
|
|
} // namespace backend
|
|
|
|
// The SystemClock represents an unsteady, monotonic clock.
|
|
//
|
|
// The epoch of this clock is unspecified and may not be related to wall time
|
|
// (for example, it can be time since boot). The time between ticks of this
|
|
// clock may vary due to sleep modes and potential interrupt handling.
|
|
// SystemClock meets the requirements of C++'s TrivialClock and Pigweed's
|
|
// PigweedClock.
|
|
//
|
|
// SystemClock is compatible with C++'s Clock & TrivialClock including:
|
|
// SystemClock::rep
|
|
// SystemClock::period
|
|
// SystemClock::duration
|
|
// SystemClock::time_point
|
|
// SystemClock::is_steady
|
|
// SystemClock::now()
|
|
//
|
|
// Example:
|
|
//
|
|
// SystemClock::time_point before = SystemClock::now();
|
|
// TakesALongTime();
|
|
// SystemClock::duration time_taken = SystemClock::now() - before;
|
|
// bool took_way_too_long = false;
|
|
// if (time_taken > std::chrono::seconds(42)) {
|
|
// took_way_too_long = true;
|
|
// }
|
|
//
|
|
// This code is thread & IRQ safe, it may be NMI safe depending on is_nmi_safe.
|
|
struct SystemClock {
|
|
using rep = int64_t;
|
|
// The period must be provided by the backend.
|
|
using period = std::ratio<PW_CHRONO_SYSTEM_CLOCK_PERIOD_SECONDS_NUMERATOR,
|
|
PW_CHRONO_SYSTEM_CLOCK_PERIOD_SECONDS_DENOMINATOR>;
|
|
using duration = std::chrono::duration<rep, period>;
|
|
using time_point = std::chrono::time_point<SystemClock>;
|
|
// The epoch must be provided by the backend.
|
|
static constexpr Epoch epoch = backend::kSystemClockEpoch;
|
|
|
|
// The time points of this clock cannot decrease, however the time between
|
|
// ticks of this clock may slightly vary due to sleep modes. The duration
|
|
// during sleep may be ignored or backfilled with another clock.
|
|
static constexpr bool is_monotonic = true;
|
|
static constexpr bool is_steady = false;
|
|
|
|
// The now() function may not move forward while in a critical section or
|
|
// interrupt. This must be provided by the backend.
|
|
static constexpr bool is_free_running = backend::kSystemClockFreeRunning;
|
|
|
|
// The clock must stop while in halting debug mode.
|
|
static constexpr bool is_stopped_in_halting_debug_mode = true;
|
|
|
|
// The now() function can be invoked at any time.
|
|
static constexpr bool is_always_enabled = true;
|
|
|
|
// The now() function may work in non-masking interrupts, depending on the
|
|
// backend. This must be provided by the backend.
|
|
static constexpr bool is_nmi_safe = backend::kSystemClockNmiSafe;
|
|
|
|
// This is thread and IRQ safe. This must be provided by the backend.
|
|
static time_point now() noexcept {
|
|
return time_point(duration(backend::GetSystemClockTickCount()));
|
|
}
|
|
|
|
// This is purely a helper, identical to directly using std::chrono::ceil, to
|
|
// convert a duration type which cannot be implicitly converted where the
|
|
// result is rounded up.
|
|
template <class Rep, class Period>
|
|
static constexpr duration for_at_least(std::chrono::duration<Rep, Period> d) {
|
|
return std::chrono::ceil<duration>(d);
|
|
};
|
|
|
|
// Computes the nearest time_point after the specified duration has elapsed.
|
|
//
|
|
// This is useful for translating delay or timeout durations into deadlines.
|
|
//
|
|
// The time_point is computed based on now() plus the specified duration
|
|
// where a singular clock tick is added to handle partial ticks. This ensures
|
|
// that a duration of at least 1 tick does not result in [0,1] ticks and
|
|
// instead in [1,2] ticks.
|
|
static time_point TimePointAfterAtLeast(duration after_at_least) {
|
|
return now() + after_at_least + duration(1);
|
|
}
|
|
};
|
|
|
|
// An abstract interface representing a SystemClock.
|
|
//
|
|
// This interface allows decoupling code that uses time from the code that
|
|
// creates a point in time. You can use this to your advantage by injecting
|
|
// Clocks into interfaces rather than having implementations call
|
|
// SystemClock::now() directly. However, this comes at a cost of a vtable per
|
|
// implementation and more importantly passing and maintaining references to the
|
|
// VirtualSystemCLock for all of the users.
|
|
//
|
|
// The VirtualSystemClock::RealClock() function returns a reference to the
|
|
// real global SystemClock.
|
|
//
|
|
// Example:
|
|
//
|
|
// void DoFoo(VirtualSystemClock& system_clock) {
|
|
// SystemClock::time_point now = clock.now();
|
|
// // ... Code which consumes now.
|
|
// }
|
|
//
|
|
// // Production code:
|
|
// DoFoo(VirtualSystemCLock::RealClock);
|
|
//
|
|
// // Test code:
|
|
// MockClock test_clock();
|
|
// DoFoo(test_clock);
|
|
//
|
|
// This interface is thread and IRQ safe.
|
|
class VirtualSystemClock {
|
|
public:
|
|
// Returns a reference to the real system clock to aid instantiation.
|
|
static VirtualSystemClock& RealClock();
|
|
|
|
virtual ~VirtualSystemClock() = default;
|
|
virtual SystemClock::time_point now() = 0;
|
|
};
|
|
|
|
} // namespace pw::chrono
|
|
|
|
// The backend can opt to include an inlined implementation of the following:
|
|
// int64_t GetSystemClockTickCount();
|
|
#if __has_include("pw_chrono_backend/system_clock_inline.h")
|
|
#include "pw_chrono_backend/system_clock_inline.h"
|
|
#endif // __has_include("pw_chrono_backend/system_clock_inline.h")
|
|
|
|
#endif // __cplusplus
|
|
|
|
PW_EXTERN_C_START
|
|
|
|
// C API Users should not create pw_chrono_SystemClock_Duration's directly,
|
|
// instead it is strongly recommended to use macros which express the duration
|
|
// in time units, instead of non-portable ticks.
|
|
//
|
|
// The following macros round up just like std::chrono::ceil, this is the
|
|
// recommended rounding to maintain the "at least" contract of timeouts and
|
|
// deadlines (note the *_CEIL macros are the same only more explicit):
|
|
// PW_SYSTEM_CLOCK_MS(milliseconds)
|
|
// PW_SYSTEM_CLOCK_S(seconds)
|
|
// PW_SYSTEM_CLOCK_MIN(minutes)
|
|
// PW_SYSTEM_CLOCK_H(hours)
|
|
// PW_SYSTEM_CLOCK_MS_CEIL(milliseconds)
|
|
// PW_SYSTEM_CLOCK_S_CEIL(seconds)
|
|
// PW_SYSTEM_CLOCK_MIN_CEIL(minutes)
|
|
// PW_SYSTEM_CLOCK_H_CEIL(hours)
|
|
//
|
|
// The following macros round down like std::chrono::{floor,duration_cast},
|
|
// these are discouraged but sometimes necessary:
|
|
// PW_SYSTEM_CLOCK_MS_FLOOR(milliseconds)
|
|
// PW_SYSTEM_CLOCK_S_FLOOR(seconds)
|
|
// PW_SYSTEM_CLOCK_MIN_FLOOR(minutes)
|
|
// PW_SYSTEM_CLOCK_H_FLOOR(hours)
|
|
#include "pw_chrono/internal/system_clock_macros.h"
|
|
|
|
typedef struct {
|
|
int64_t ticks;
|
|
} pw_chrono_SystemClock_Duration;
|
|
|
|
typedef struct {
|
|
pw_chrono_SystemClock_Duration duration_since_epoch;
|
|
} pw_chrono_SystemClock_TimePoint;
|
|
typedef int64_t pw_chrono_SystemClock_Nanoseconds;
|
|
|
|
// Returns the current time, see SystemClock::now() for more detail.
|
|
pw_chrono_SystemClock_TimePoint pw_chrono_SystemClock_Now(void);
|
|
|
|
// Returns the change in time between the current_time - last_time.
|
|
pw_chrono_SystemClock_Duration pw_chrono_SystemClock_TimeElapsed(
|
|
pw_chrono_SystemClock_TimePoint last_time,
|
|
pw_chrono_SystemClock_TimePoint current_time);
|
|
|
|
// For lossless time unit conversion, the seconds per tick ratio that is
|
|
// numerator/denominator should be used:
|
|
// PW_CHRONO_SYSTEM_CLOCK_PERIOD_SECONDS_NUMERATOR
|
|
// PW_CHRONO_SYSTEM_CLOCK_PERIOD_SECONDS_DENOMINATOR
|
|
|
|
// Warning, this may be lossy due to the use of std::chrono::floor,
|
|
// rounding towards zero.
|
|
pw_chrono_SystemClock_Nanoseconds pw_chrono_SystemClock_DurationToNsFloor(
|
|
pw_chrono_SystemClock_Duration duration);
|
|
|
|
PW_EXTERN_C_END
|