This thread has been locked.

If you have a related question, please click the "Ask a related question" button in the top right corner. The newly created question will be automatically linked to this question.

Compiler/MSP430FR5969: Error #28 expression must have a constant value when using CLOCKS_PER_SEC

Part Number: MSP430FR5969

Tool/software: TI C/C++ Compiler

I am having a heck of a time implementing a std::chrono compatible time-keeping system.

I am trying to redefine the C function clock() ( following  )

Why isn't CLOCKS_PER_SEC constant / OR how can I redefine std::chrono::steady_clock::now() to return nanosecond time_point's. The current implementation divides clock() by CLOCKS_PER_SEC, casts that value to seconds, and then converts that back to nanoseconds.

My real problem is that implementing my own std::chrono::steady_clock::now() funtion causes a redefinition of symbols error with TI-Compiler 

Is there a way to override a single linked-library function? Our codebase requires the use std::chrono::steady_clock and cannot use a custom clock implementation.

    1. CLOCKS_PER_SEC is not a constant because many embedded processors can run at different clock rates.  We can't know the clock rate at compilation time, so it is left as a variable that is supposed to be populated at startup time.
    2. You can modify the implementation of any function, but you must provide a definition for every function defined in that module.  Copy the file $(COMPILER_ROOT)/lib/src/libcxx/chrono.cpp into your project and modify the definition of std::chrono::steady_clock::now.  You should get a clean compilation with no duplicated symbols.
    3. You want a timer with nanosecond resolution, but unfortunately the TI compiler's implementation of steady_clock::now truncates to whole second resolution before returning nanoseconds.  Unfortunately, it's not easy to perform the conversion with high precision without using floating point arithmetic, which will make the function much slower.  I can explain this in more detail, but I doubt you're interested in that.  Suffice it to say there is no single implementation that will be perfect in every way for every ISA, so we've used an implementation that does not use floating-point arithmetic and cannot overflow.
    4. I'm going to suggest you compute nanoseconds with the expression (clock() * 1000 / (CLOCKS_PER_SEC / 1000 / 1000)).  This avoids floating-point arithmetic, and if you can be assured that CLOCKS_PER_SEC will always be a multiple of 1 million, you'll get nanosecond-accurate values.  The numerator in this expression can overflow, but on MSP430 with CLOCKS_PER_SEC==200 000 000, it will take 553 days. If you need more time than that, you are probably going to be stuck with floating-point arithmetic.

  • 1. It seems like an odd decision to make it a linked symbol and then always set it to 200MHz (for MSP430 etc). The definitions are already provided in $(COMPILER_ROOT)/lib/src/clock.c as DEFAULT_CLOCKS_PER_SEC; It would make far more sense (to me) to use that definition instead of linking it in with the ABI, but I guess I don't know the whole story. If I knew what it evaluated to at compilation time, I could write a static assertion to force our implementation to track the definition of CLOCKS_PER_SEC. 

    2. It's interesting that you mention including the chrono.cpp file with my own definitions. How is this different than supplying my own definition for just std::chrono::steady_clock::now symbol?
    From my understanding, the mangled output is the same, but provides a redefinition error because both symbols have an implicitly strong definition. Wouldn't this also be the case in providing the entire class definition?

    3. It would be really nice if the definitions were provided with some standard way to redefine them, would weak attribute functions break anything?

    4. I found a workaround using TI-Compiler's linker command line option
    --symbol_map'_ZNSt3__26chrono12steady_clock3nowEv=steady_now_patch' and providing my own definition with steady_now_patch, but this solution doesn't sit well with me. I'm going to include chrono.cpp in our source and see if I can modify the definition there, this seems like a complete solution to our problem.

    Thank you very much, I'm going to run a few tests and mark this as resolved if I can get the function redefined with the addition of a modified chrono source file

    1. Well, it's not always supposed to be 200 000 000, but the compiler has no idea what the right value is.  It is up to either the user or the operating system (or an agent on behalf of one or the other, such as the CCS project configuration) to arrange to set the value.
    2. You get the multiple definition problem because although you provided a definition for steady_clock::now, and thus the linker need not search the library for that symbol, some other part of your program decided it needed some other function, probably a different function in steady_clock.  To resolve that, it had to pull in the entire object file for chrono.cpp, which includes the default copy of steady_clock::now, and presto you have two definitions.  Weak symbols would not resolve this issue.  To avoid the problem, you have to avoid letting the linker grab chrono.cpp.obj from the library, and that means you must provide all of those definitions in your own project.  The easiest way to do so is to just drop chrono.cpp into your project.
    3. The C++ standard provides no "standard" means of replacing library functions, so any scheme to replace the functions will necessarily be implementation-specific.  For TI compilers, the implementation-specific method is pretty much to copy the file into your project and modify as needed.  If that is unacceptable, the fallback is to modify the source code for the library to your satisfaction in situ, and recompile all the libraries.
    4. Sure, that will work, but you'll have two copies of the function, one of which (the default version from the library) is never called.

  • I wasn't aware the "copy in the source file" was even an option. I assumed that I would run into the same redefinition problem, but everything seems to be working perfectly now. I was able to include the modified source and now everything in our codebase is working magically.

    Thank you very much for your time and your expertise, it was genuinely helpful
    -Quinn