diff --git a/Sming/Arch/Esp8266/Components/esp8266/include/esp_clk.h b/Sming/Arch/Esp8266/Components/esp8266/include/esp_clk.h new file mode 100644 index 0000000000..ab341bba08 --- /dev/null +++ b/Sming/Arch/Esp8266/Components/esp8266/include/esp_clk.h @@ -0,0 +1,22 @@ +#pragma once + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +// system_get_cpu_frequency is just a wrapper for this ROM function. +uint8_t ets_get_cpu_frequency(void); + +__forceinline static uint32_t esp_get_ccount() +{ + uint32_t ccount; + __asm__ __volatile__("rsr %0, ccount\n" : "=a"(ccount) : : "memory"); + return ccount; +} + +#ifdef __cplusplus +} +#endif diff --git a/Sming/Arch/Esp8266/Components/gdbstub/gdbuart.cpp b/Sming/Arch/Esp8266/Components/gdbstub/gdbuart.cpp index 6ed3f7c7b8..6a6a6eaca9 100644 --- a/Sming/Arch/Esp8266/Components/gdbstub/gdbuart.cpp +++ b/Sming/Arch/Esp8266/Components/gdbstub/gdbuart.cpp @@ -12,11 +12,11 @@ #include "gdbuart.h" #include "GdbPacket.h" -#include "driver/uart.h" -#include "driver/SerialBuffer.h" -#include "Platform/System.h" -#include "HardwareSerial.h" -#include +#include +#include +#include +#include +#include #include "gdbsyscall.h" #define GDB_UART UART0 // Only UART0 supports for debugging as RX/TX required @@ -109,14 +109,7 @@ static size_t ATTR_GDBEXTERNFN gdb_uart_write_char(char c) int ATTR_GDBEXTERNFN gdbReceiveChar() { -#if GDBSTUB_UART_READ_TIMEOUT - constexpr uint32_t timeout = round(double(HW_TIMER2_CLK) * GDBSTUB_UART_READ_TIMEOUT / 1000); - auto startTicks = NOW(); -#define checkTimeout() (NOW() - startTicks >= timeout) -#else -#define checkTimeout() (false) -#endif - + OneShotElapseTimer timer(GDBSTUB_UART_READ_TIMEOUT); do { wdt_feed(); system_soft_wdt_feed(); @@ -127,7 +120,7 @@ int ATTR_GDBEXTERNFN gdbReceiveChar() #endif return c; } - } while(!checkTimeout()); + } while(GDBSTUB_UART_READ_TIMEOUT == 0 || !timer.expired()); return -1; } diff --git a/Sming/Arch/Host/Components/esp_hal/clk.c b/Sming/Arch/Host/Components/esp_hal/clk.c index eac810203d..74729ee259 100644 --- a/Sming/Arch/Host/Components/esp_hal/clk.c +++ b/Sming/Arch/Host/Components/esp_hal/clk.c @@ -1,19 +1,42 @@ #include "include/esp_clk.h" +#include "include/esp_system.h" -// The current CPU frequency -static uint8_t __cpu_frequency = 80; +// The current CPU frequency in MHz (ticks per us) +static uint8_t cpu_frequency = SYS_CPU_80MHZ; bool system_update_cpu_freq(uint8 freq) { - if(freq == 80 || freq == 160) { - __cpu_frequency = freq; + if(freq == SYS_CPU_80MHZ || freq == SYS_CPU_160MHZ) { + cpu_frequency = freq; return true; } else { return false; } } +uint8_t ets_get_cpu_frequency(void) +{ + return cpu_frequency; +} + uint8 system_get_cpu_freq(void) { - return __cpu_frequency; + return ets_get_cpu_frequency(); +} + +/* + * The 'correct' conversion is actually: + * + * `os_get_nanoseconds() / (1000UL * cpu_frequency)` + * + * However, in use this just ends up returning 0 all the time which is + * not particularly useful. + * + * On my dev. system a straight nanosecond count gives quite useful + * values when evaluating code paths. Try :sample:`Basic_Delegates`. + * + */ +uint32_t esp_get_ccount() +{ + return os_get_nanoseconds(); } diff --git a/Sming/Arch/Host/Components/esp_hal/include/esp_clk.h b/Sming/Arch/Host/Components/esp_hal/include/esp_clk.h index ab707dc969..e85ccf4772 100644 --- a/Sming/Arch/Host/Components/esp_hal/include/esp_clk.h +++ b/Sming/Arch/Host/Components/esp_hal/include/esp_clk.h @@ -11,7 +11,11 @@ extern "C" { #define SYS_CPU_160MHZ 160 bool system_update_cpu_freq(uint8 freq); -uint8 system_get_cpu_freq(void); +uint8_t ets_get_cpu_frequency(void); +uint8_t system_get_cpu_freq(void); + +/* Emulation of CPU cycle count */ +uint32_t esp_get_ccount(); #ifdef __cplusplus } diff --git a/Sming/Arch/Host/Components/esp_hal/system.cpp b/Sming/Arch/Host/Components/esp_hal/system.cpp index dd810daa16..6e03336885 100644 --- a/Sming/Arch/Host/Components/esp_hal/system.cpp +++ b/Sming/Arch/Host/Components/esp_hal/system.cpp @@ -2,7 +2,7 @@ #include #include #include -#include +#include /* System time */ @@ -54,8 +54,8 @@ uint32_t system_get_time() void os_delay_us(uint32_t us) { - auto start = system_get_time(); - while(system_get_time() - start < us) { + ElapseTimer timer(us); + while(!timer.expired()) { // } } diff --git a/Sming/Arch/Host/Components/hostlib/startup.cpp b/Sming/Arch/Host/Components/hostlib/startup.cpp index 2359127d99..74ef3672a2 100644 --- a/Sming/Arch/Host/Components/hostlib/startup.cpp +++ b/Sming/Arch/Host/Components/hostlib/startup.cpp @@ -26,7 +26,6 @@ #include "options.h" #include #include -#include #include #include #include @@ -34,6 +33,7 @@ #include #include +#include static int exitCode = 0; static bool done = false; @@ -237,15 +237,13 @@ int main(int argc, char* argv[]) init(); - const uint32_t lwipServiceInterval = 50000; - uint32_t lwipNextService = 0; + OneShotElapseTimer lwipServiceTimer(50); while(!done) { - auto now = system_get_time(); host_service_tasks(); host_service_timers(); - if(lwip_initialised && (now >= lwipNextService)) { + if(lwip_initialised && lwipServiceTimer.expired()) { host_lwip_service(); - lwipNextService = now + lwipServiceInterval; + lwipServiceTimer.start(); } system_soft_wdt_feed(); } diff --git a/Sming/Core/NanoTime.cpp b/Sming/Core/NanoTime.cpp new file mode 100644 index 0000000000..a50681409a --- /dev/null +++ b/Sming/Core/NanoTime.cpp @@ -0,0 +1,145 @@ +/**** + * Sming Framework Project - Open Source framework for high efficiency native ESP8266 development. + * Created 2015 by Skurydin Alexey + * http://github.com/SmingHub/Sming + * All files of the Sming Core are provided under the LGPL v3 license. + * + * NanoTime.cpp + * + * @author mikee47 + * + ****/ + +#include "NanoTime.h" + +#include + +namespace NanoTime +{ +const char* unitToString(Unit unit) +{ + switch(unit) { + case Nanoseconds: + return "ns"; + case Microseconds: + return "us"; + case Milliseconds: + return "ms"; + case Seconds: + return "s"; + case Minutes: + return "m"; + case Hours: + return "h"; + case Days: + return "d"; + default: + return "?s"; + } +} + +const char* unitToLongString(Unit unit) +{ + switch(unit) { + case Nanoseconds: + return "nanoseconds"; + case Microseconds: + return "microseconds"; + case Milliseconds: + return "milliseconds"; + case Seconds: + return "seconds"; + case Minutes: + return "minutes"; + case Hours: + return "hours"; + case Days: + return "days"; + default: + return "?s"; + } +} + +String Frequency::toString() const +{ + auto freq = frequency; + unsigned div = 0; + while(freq % 1000 == 0) { + freq /= 1000; + ++div; + } + String s(freq); + if(div == 1) { + s += 'K'; + } else if(div == 2) { + s += 'M'; + } else if(div == 3) { + s += 'G'; + } + s += "Hz"; + return s; +} + +template class FormatBuffer +{ +public: + FormatBuffer() + { + buffer[0] = '\0'; + } + + void add(unsigned value, unsigned digits) + { + pos += strlen(ultoa_wp(value, &buffer[pos], 10, digits, '0')); + } + + void add(char c) + { + buffer[pos++] = c; + } + + operator String() const + { + return String(buffer, pos); + } + +private: + char buffer[BufSize]; + unsigned pos = 0; +}; + +String TimeValue::toString() const +{ + if(overflow) { + return "(OVF)"; + } + + FormatBuffer<64> buf; + + if(days != 0) { + buf.add(days, 0); + buf.add('d'); + buf.add(' '); + } + + buf.add(hours, 2); + buf.add(':'); + buf.add(minutes, 2); + buf.add(':'); + buf.add(seconds, 2); + + if(unit < NanoTime::Seconds) { + buf.add('.'); + buf.add(milliseconds, 3); + if(microseconds != 0 || nanoseconds != 0) { + buf.add(microseconds, 3); + if(nanoseconds != 0) { + buf.add(nanoseconds, 3); + } + } + } + + return buf; +} + +} // namespace NanoTime diff --git a/Sming/Core/NanoTime.h b/Sming/Core/NanoTime.h new file mode 100644 index 0000000000..143e0b16b1 --- /dev/null +++ b/Sming/Core/NanoTime.h @@ -0,0 +1,718 @@ +/**** + * Sming Framework Project - Open Source framework for high efficiency native ESP8266 development. + * Created 2015 by Skurydin Alexey + * http://github.com/SmingHub/Sming + * All files of the Sming Core are provided under the LGPL v3 license. + * + * NanoTime.h - Utilities for handling time periods at nanosecond resolution + * + * @author mikee47 + * + * Note: C++ provides the `chrono` utilities but is not well-suited for embedded applications: + * + * - Only uses 64-bit calculations. NanoTime can use any integral type, although uint32_t is the most useful. + * - Time conversions are truncated. A value of 0.99 seconds would be treated as 0. + * NanoTime rounds results so would return 1. + * - Supports compile-time calculations but no runtime calculation support. + * + ****/ + +#pragma once + +#include +#include +#include +#include +#include "Rational.h" +#include + +/** @defgroup timer Timers + * @brief System timer support classes +*/ + +/** @defgroup system_clocks System clocks + * @brief System clock definitions + * @ingroup timer + * @{ +*/ + +namespace NanoTime +{ +/** + * @brief Identify units for a scalar quantity of time + * @note Ordered in increasing unit size, e.g. days > seconds + */ +enum Unit { + Nanoseconds, + Microseconds, + Milliseconds, + Seconds, + Minutes, + Hours, + Days, + UnitMax = Days, +}; + +/** + * @brief Get a string identifying the given time units, e.g. "ns" + */ +const char* unitToString(Unit unit); + +/** + * @brief Get a long string identifying the given time units, e.g. "seconds" + */ +const char* unitToLongString(Unit unit); + +/** + * @brief Class to represent a frequency + */ +struct Frequency { + Frequency(uint32_t frequency) : frequency(frequency) + { + } + + operator uint32_t() + { + return frequency; + } + + /** + * @brief Get frequency as compact string + * @note Drop trailing zeroes to produce a more compact string, e.g. + * 0 -> 0Hz + * 1000 -> 1KHz + * 4553000 -> 4553KHz + * 1000000000 -> 1GHz + * + */ + String toString() const; + + uint32_t frequency; +}; + +/** + * @brief List of clock ticks for each supported unit of time + */ +constexpr BasicRatio32 unitTicks[UnitMax + 1] = { + {1000000000, 1}, // Nanoseconds + {1000000, 1}, // Microseconds + {1000, 1}, // Milliseconds + {1, 1}, // Seconds + {1, 60}, // Minutes + {1, 60 * 60}, // Hours + {1, 24 * 60 * 60}, // Days +}; + +/** + * @brief Class template to define tick std::ratio type + * @tparam unit + * @retval std::ratio Ticks per second + * @note This would be preferable: + * `template using UnitTickRatio = std::ratio;` + * But GCC 4.8 doesn't like it (lvalue required as unary '&' operand) + */ +template struct UnitTickRatio { + static constexpr uint64_t num = unitTicks[unit].num; + static constexpr uint64_t den = unitTicks[unit].den; +}; + +// Forward declarations +template struct TimeConst; +template struct TicksConst; +template struct TimeSource; +template struct Time; +template struct Ticks; + +/** + * @brief Class template representing a physical Clock with fixed timing characteristics + * @tparam ClockDef The actual Clock being constructed (so we can query its properties) + * @tparam frequency_ Clock frequency in Hz + * @tparam TickType_ Variable type representing the clock's tick value + * @tparam maxTicks_ Maximum count value for the clock + * @note Physical clocks are implemented using this as a base. + * The provided frequency accounts for any prescaler setting in force. Fixing this + * at compile time helps to avoid expensive runtime calculations and permits static range checks. + * @see Use `TimeSource` to work with a Clock in specific time units + */ +template struct Clock { + using TickType = TickType_; + template using TicksConst = TicksConst; + template using TimeConst = TimeConst; + template using TicksPerUnit = std::ratio_divide, UnitTickRatio>; + template using TimeSource = TimeSource; + template using Ticks = Ticks; + + static constexpr const char* typeName() + { + return ClockDef::typeName(); + } + + static constexpr uint32_t frequency() + { + return frequency_; + } + + using MaxTicks = TicksConst; + static constexpr MaxTicks maxTicks() + { + return MaxTicks(); + } + + template using MaxTime = typename MaxTicks::template TimeConst; + template static constexpr MaxTime maxTime() + { + return MaxTime(); + } + + /** + * @brief Get ticks per unit as a Ratio object + * @retval BasicRatio32 + */ + static Ratio32 ticksPerUnit(Unit unit) + { + const auto& tpu = unitTicks[unit]; + return Ratio32(frequency_ * tpu.den, tpu.num); + } + + /** + * @brief Class template defining a fixed time quantity + * @tparam time + * @retval TimeConst + */ + template static constexpr TimeConst timeConst() + { + return TimeConst(); + } + + /** + * @brief Class template defining a fixed tick quantity + * @tparam ticks + * @retval TicksConst + */ + template static constexpr TicksConst ticksConst() + { + return TicksConst(); + } + + /** + * @brief Create a Time Source for this Clock + * @tparam unit + * @tparam TimeType + */ + template static constexpr TimeSource timeSource() + { + return TimeSource(); + } + + /** + * @brief Get the number of ticks for a given time + * @param time + * @retval TimeType Tick count, rounded to the nearest tick + */ + template static Ticks timeToTicks(TimeType time) + { + return TimeSource::timeToTicks(time); + } + + /** + * @brief Get the time for a given number of clock ticks + * @param ticks + * @retval TimeType Time count, rounded to the nearest unit + */ + template static Time ticksToTime(TimeType ticks) + { + return TimeSource::ticksToTime(ticks); + } + + static String toString() + { + String s; + s += typeName(); + s += '/'; + s += Frequency(frequency()).toString(); + return s; + } +}; + +/** + * @brief Function template to convert a constant time quantity from one unit to another + * @param time The time to convert + * @tparam unitsFrom Units for `time` parameter + * @tparam unitsTo Units for return value + * @retval TimeType Converted time + * @note example: + * + * uint32_t micros = convert<50, Milliseconds, Microseconds>(); + */ +template constexpr uint64_t convert() +{ + return ({ + using R = std::ratio_divide, UnitTickRatio>; + round(double(time) * R::num / R::den); + }); +} + +/** + * @brief Function template to convert a time quantity from one unit to another + * @tparam TimeType Variable type to use for calculation + * @param time The time to convert + * @param unitsFrom Units for `time` parameter + * @param unitsTo Units for return value + * @retval TimeType Converted time, returns TimeType(-1) if calculation overflowed + */ +template __forceinline TimeType convert(const TimeType& time, Unit unitsFrom, Unit unitsTo) +{ + if(unitsFrom == unitsTo) { + return time; + } + + using R = Ratio; + return time * R(unitsTo) / R(unitsFrom); +} + +/** + * @brief A time time broken into its constituent elements + * @note Useful for analysing and printing time values + */ +struct TimeValue { + TimeValue() = default; + + /** + * @brief Resolve a time value into constituent components + * @param time The time to resolve + * @param unit Units for given time + */ + template TimeValue(Unit unit, TimeType time) + { + set(unit, time); + } + + template void set(Unit unit, TimeType time); + + /** + * @brief Get sub-second time entirely in microseconds + */ + uint32_t getMicroseconds() const + { + return microseconds + (milliseconds * 1000); + } + + /** + * @brief Get sub-second time entirely in nanoseconds + */ + uint32_t getNanoseconds() const + { + return nanoseconds + getMicroseconds() * 1000; + } + + String toString() const; + + operator String() const + { + return toString(); + } + + bool overflow = false; + Unit unit = Seconds; ///< Time unit passed to set() call + uint32_t days = 0; + uint8_t hours = 0; + uint8_t minutes = 0; + uint8_t seconds = 0; + uint16_t milliseconds = 0; + uint32_t microseconds = 0; + uint32_t nanoseconds = 0; +}; + +template void TimeValue::set(Unit unit, TimeType time) +{ + overflow = (time == TimeType(-1)); + this->unit = unit; + + TimeType elem[UnitMax + 1] = {0}; + elem[unit] = time; + + auto divmod = [&elem](Unit u, uint16_t div) { + elem[u + 1] = elem[u] / div; + elem[u] %= div; + }; + + if(unit < Days) { + if(unit < Hours) { + if(unit < Minutes) { + if(unit < Seconds) { + if(unit < Milliseconds) { + if(unit < Microseconds) { + divmod(Nanoseconds, 1000); + } + divmod(Microseconds, 1000); + } + divmod(Milliseconds, 1000); + } + divmod(Seconds, 60); + } + divmod(Minutes, 60); + } + divmod(Hours, 24); + } + + nanoseconds = elem[Nanoseconds]; + microseconds = elem[Microseconds]; + milliseconds = elem[Milliseconds]; + seconds = elem[Seconds]; + minutes = elem[Minutes]; + hours = elem[Hours]; + days = elem[Days]; +} + +/** + * @brief Class to handle a simple time value with associated unit + */ +template struct Time { + Time(Unit unit, T time) : unit(unit), time(time) + { + } + + operator T() const + { + return time; + } + + String toString() const + { + String s(time); + s += unitToString(unit); + return s; + } + + TimeValue value() const + { + return TimeValue(unit, time); + } + + template Time as() const + { + return Time(unitTo, convert(time, unit, unitTo)); + } + + Unit unit; + T time; +}; + +/** + * @brief Helper function to create a Time and deduce the type + */ +template Time time(Unit unit, T value) +{ + return Time(unit, value); +} + +/** + * @brief Class to handle a tick value associated with a clock + */ +template struct Ticks { + using Clock = Clock_; + + static constexpr Clock clock() + { + return Clock(); + } + + Ticks(T ticks) : ticks(ticks) + { + } + + operator T() const + { + return ticks; + } + + String toString() const + { + return String(ticks); + } + + template Time as() + { + return Time(unit, Clock::template ticksToTime(ticks)); + } + + T ticks; +}; + +/** + * @brief Class template to represent a fixed time value for a specific Clock + * @tparam Clock_ + * @tparam unit_ + * @tparam time_ + * @note Includes compile-time range checking. Time is taken as reference for conversions. + */ +template struct TimeConst { + using Clock = Clock_; + using TicksPerUnit = typename Clock::template TicksPerUnit; + + static constexpr Clock clock() + { + return Clock(); + } + + static constexpr Unit unit() + { + return unit_; + } + + static constexpr uint64_t time() + { + return time_; + } + + constexpr operator uint64_t() + { + return time_; + } + + /** + * @brief Get ticks per unit as a Ratio object + * @retval BasicRatio32 + */ + static Ratio32 ticksPerUnit() + { + return BasicRatio32{TicksPerUnit::num, TicksPerUnit::den}; + } + + /** + * @brief Return the corresponding tick value for the time interval + * @retval TimeType + */ + static constexpr uint64_t ticks() + { + return round(double(time_) * TicksPerUnit::num / TicksPerUnit::den); + } + + /** + * @brief Use this function to perform a static (compile-time) range check against Clock maxTicks + * @retval TimeType + */ + static constexpr void check() + { + static_assert(ticks() <= Clock::maxTicks(), "Time exceeds clock range"); + } + + /** + * @brief Obtain the actual Clock time by converting tick value + * @retval TimeType + */ + static constexpr uint64_t clockTime() + { + return round(double(ticks()) * TicksPerUnit::den / TicksPerUnit::num); + } + + /** + * @brief Obtain the time in a different set of units + * @tparam unit + * @retval TimeType + */ + template static constexpr uint64_t as() + { + return convert(); + } + + static TimeValue value() + { + return TimeValue(unit_, time_); + } + + static TimeValue clockValue() + { + return TimeValue(unit_, clockTime()); + } + + static String toString() + { + return Time(unit_, time_).toString(); + } +}; + +/** + * @brief Class template representing a fixed clock tick count + * @tparam Source_ + * @tparam ticks_ + * @note Includes compile-time range checking + */ +template struct TicksConst { + using Clock = Clock_; + using TickType = uint64_t; + using TimeType = uint64_t; + + static constexpr TickType ticks() + { + return ticks_; + } + + constexpr operator TickType() + { + return ticks_; + } + + /** + * @brief Obtain the tick count with a static range check against Clock maxTicks + * @retval TimeType + */ + static constexpr void check() + { + static_assert(ticks_ <= Clock::maxTicks(), "Ticks exceeds clock range"); + } + + template + using TimeConst = TimeConst::den / + Clock::template TicksPerUnit::num))>; + + /** + * @brief Get the time for the tick count in a specific time unit + * @tparam unit + * @retval Time + */ + template static constexpr TimeConst as() + { + return TimeConst(); + } + + static String toString() + { + return ticks_; + } +}; + +/** + * @brief Function template to convert a time quantity from one unit to another + * @tparam unitsFrom Units for `time` parameter + * @tparam unitsTo Units for return value + * @tparam TimeType Variable type to use for calculation + * @param time The time to convert + * @retval TimeType Converted time, returns TimeType(-1) if calculation overflowed + */ +template __forceinline TimeType convert(const TimeType& time) +{ + if(unitsFrom == unitsTo) { + return time; + } + + using R = std::ratio_divide, UnitTickRatio>; + return muldiv(time); +} + +/** + * @brief Class template for accessing a Clock in specific time units + * @tparam Clock_ + * @tparam units_ + * @tparam TimeType_ Limits range of calculations + * @note Includes compile-time range checking. Time is taken as reference for conversions. + */ +template struct TimeSource : public Clock_ { + using Clock = Clock_; + using TimeType = TimeType_; + using TicksPerUnit = typename Clock::template TicksPerUnit; + template using TimeConst = TimeConst; + template using TicksConst = TicksConst; + + static constexpr Unit unit() + { + return unit_; + } + + /** + * @brief Number of clock ticks per unit of time + * @retval BasicRatio32 Result as a rational fraction + */ + static constexpr BasicRatio32 ticksPerUnit() + { + return {TicksPerUnit::num, TicksPerUnit::den}; + } + + /** + * @brief Get the time corresponding to the maximum clock tick value + */ + using MaxClockTime = typename Clock::template MaxTime; + static constexpr MaxClockTime maxClockTime() + { + return MaxClockTime(); + } + + // Check against arbitrary minimum - could be 1, but is that actually useful? + static_assert(maxClockTime() >= 5, "Time units too large for Clock "); + + /** + * @brief Obtain a TimeConst type representing the given time quantity + * @tparam time + * @retval TimeConst + * @note Use methods of TimeConst to obtain corresponding tick count, etc. + */ + template static constexpr TimeConst