2
votes

I am using arm-none-eabi toolchain with newlib to target a custom board with an ARM Cortex-M0+ (specifically the MCU-on-eclipse version of the toolchain). I am compiling/linking with -nostartfiles and --specs=nano.specs and have re-targeted stdout and stderr to USB and a serial port respectively. I have created implementations for most of the C system calls.

I am using the chrono library with two custom clock, the now() functions get RTC time or my systick timer. It seems like this mirrors the purpose of the standard steady_clock and system_clock and so I though I could try using them.

to do so I had to implement the gettimeofday syscall which I did

// returning a set time of one second just for testing
int _gettimeofday(struct timeval* tv, void* tz) {
    tv->tv_sec  = 1;
    tv->tv_usec = 255;
    return 0;
}

my main code is as follows:

int main(void)
{
    HWInit();

    static std::chrono::steady_clock::time_point t1 = std::chrono::steady_clock::now();
    static std::chrono::system_clock::time_point t2 = std::chrono::system_clock::now();
    int64_t count1 = t1.time_since_epoch().count();
    int64_t count2 = t2.time_since_epoch().count();

    printf("Time 1: %lld\n Time 2: %lld\n", count1, count2);
    for(;;){}
    return 0;
}

using the debugger I can see that both steady_clock::now() and sysytem_clock::now() call my _gettimeofday() function and both end up with the exact same time-point.

of course if I try to do the following I get multiple definition errors:

using SysClock = std::chrono::system_clock;

SysClock::time_point SysClock::now() noexcept {
    return SysClock::time_point( SysClock::duration(1983) );
}

So can I somehow overload the now() functions of the standard chrono clocks? or maybe the entire clock implementation with my own duration and rep typedefs that match the hardware better? I can overload new and delete for my embedded system (and should), so doing this for chrono would also be nice.

2

2 Answers

2
votes

From gccs libstdc++ chrono.cc:

  • system_clock::now() uses gettimeofday(&tv, 0); or clock_gettime(CLOCK_REALTIME, &tp); or syscall. If gettimeofday works for you, that means it uses it.
  • steady_clock::now() uses clock_gettime(CLOCK_MONOTONIC, &tp);. So you should overload clock_gettime and handle CLOCK_MONOTONIC argument.
  • There is no _clock_gettime_r function provided by newlib, as one in _gettimeofday_t that passes newlib's struct reent around. If you want to handle multithreading within newlib, it's good to write your own similar wrapper that handles _reent->errno value. But the bet would be to overload _gettimeofday_r function as you aim only at newlib.
0
votes

Instead of trying to change the behavior of system_clock and steady_clock, I recommend just writing your own custom clocks and using them. That way you can better tailor them to your hardware and needs. If you have some way to get the current time, creating a custom chrono clock to wrap that function is very easy.

class SysClock
{
public:
    // 500MHz, or whatever you need
    using period                    = std::ratio<1, 500'000'000>;
    using rep                       = long long;
    using duration                  = std::chrono::duration<rep, period>;
    using time_point                = std::chrono::time_point<SysClcok>;
    static constexpr bool is_steady = true;

    static time_point now() noexcept
    {
        return time_point{duration{
            /*turn SysTick_getValue() into the number of ticks since epoch*/}};
    }
};

Now use SysClock::now() in your code instead of system_clock::now(). This gives you SysClock::time_point and chrono::durations result from the subtraction of two SysClock::time_points.

If you can turn your low-level "now" into a count of ticks against some epoch, and you can describe those ticks as a compile-time fraction of a second with period, then you're good to go.