[En-Nut-Discussion] NutGetTickClock rounding (again)
Bernard Fouché
bernard.fouche at kuantic.com
Tue Jul 15 16:01:47 CEST 2008
Henrik, if ms is directly multiplied by 'Ticks in Hertz', then the
maximum value of argument 'ms' has to be ((2^32)-1)/'Ticks in Hertz'.
If there are 1000 ticks per seconds, that's about 1 hour and 11 minutes,
very far from 49 days so already in that function the overflow problem
is to be handled.
I think that the 'arm arch' function is good, however I wonder why the
person who wrote the code split the choice between the two calculus
with 0x3E8000. If overflow is to be avoided because of a multiplication
by 1000, then it should be ((2^32)-1)/1000 (4294967 in decimal, or hex
0x418937. Using 1024 ticks per second would give 0x400000).
Now the rounding problem comes from having a number of ticks per second
slightly lower than the divisor (1000). I don't think that adding 500
fixes the problem, since you then have rounding errors if you have a
number of ticks slightly lower than 500 :)
What about (for the second calculus, the first being kept 'as is' even
if 0x3E8000 is still 'magical'):
((ms * NutGetTickClock()) + (NutGetTickClock ()/2)) / 1000UL;
Bernard
ennut at thezachariasgroup.com wrote:
> In the ARM arch the following code works well:
>
> /*!
> * \brief Calculate system ticks for a given number of milliseconds.
> */
> u_long NutTimerMillisToTicks(u_long ms) {
> if (ms >= 0x3E8000) {
> return (ms / 1000) * NutGetTickClock();
> }
> else {
> return (ms * NutGetTickClock()) / 1000;
> }
> }
>
>
> At 07:11 AM 7/15/2008, you wrote:
>
>> Bernhard,
>>
>>
>>> - If 'ms' is 'big', and if the compiler evaluates the multiplication
>>>
>> first,
>>
>>> then an overflow can happen.
>>>
>> The problem with the current implementation is not an overflow issue but a
>> rounding issue. It is simply a matter of applying proper rounding to an
>> integer division. An overflow issue only occurs if "ms" is > 49.7 days (for
>> a typical NutGetTickClock of ~1000).
>>
>> However it is probably a good suggestion to document the potential overflow
>> for NutTimerMillisToTicks and add a caveat to its documentation.
>>
>> However my concern with function NutTimerMillisToTicks is small values for
>> the "ms" parameter, values between 1 and 10 ms for example.
>>
>>
>>> - since NutGetTickClock() gives a result in 'ticks per Hertz' then it
>>>
>> seems
>>
>>> more appropriate to reduce 'ms' to a value in seconds before the next
>>> calculation.
>>>
>> In theory this is correct, but as we don't have floating point math,
>> impracticable.
>>
>> Your implementation would return 1 for all ms values < 1000 as anything
>> smaller than 1000 divided by 1000 equals to 0 in integer land.
>>
>> Henrik
>>
>>
>>> -----Original Message-----
>>> From: en-nut-discussion-bounces at egnite.de [mailto:en-nut-discussion-
>>> bounces at egnite.de] On Behalf Of Bernard Fouché
>>> Sent: Tuesday, 15 July 2008 7:04 PM
>>> To: Ethernut User Chat (English)
>>> Subject: Re: [En-Nut-Discussion] NutGetTickClock rounding (again)
>>>
>>> What about:
>>>
>>> u_long NutTimerMillisToTicks(u_long ms)
>>> {
>>> u_long x;
>>>
>>> x = ( ms/1000UL ) * NutGetTickClock();
>>> if (x == 0) {
>>> x = 1;
>>> }
>>>
>>> return (x);
>>> }
>>>
>>> This because:
>>>
>>> - If 'ms' is 'big', and if the compiler evaluates the multiplication
>>>
>> first,
>>
>>> then an overflow can happen.
>>>
>>> - since NutGetTickClock() gives a result in 'ticks per Hertz' then it
>>>
>> seems
>>
>>> more appropriate to reduce 'ms' to a value in seconds before the next
>>> calculation.
>>>
>>> Bernard
>>>
>>> Henrik Maier wrote:
>>>
>>>> There has be some discussion about a better implementation and bug-fix
>>>>
>> of
>>
>>>> NutTimerMillisToTicks (refer to posts in 2006, subject "Request for
>>>>
>>> Comment:
>>>
>>>> NutTimerMillisToTicks()").
>>>>
>>>> This has been applied in recent Nut/OS releases (>4.2.1 onwards).
>>>>
>>>> However the current implementation does only round for the special case
>>>>
>>> when
>>>
>>>> the result would be 0 (see if clause below). However for other cases
>>>>
>> with
>>
>>>> small ms values it does not round at all and we end up with large errors
>>>> (50%) for small ms values in cases where NutGetTickClock() is just below
>>>> 1000, for example 997. For clock frequencies where NutGetTickClock() is
>>>> greater than 1000 it does not matter but for cases where it is less than
>>>> 1000 it results in unnecessary inaccuracies.
>>>>
>>>> I came across this issue when recently upgrading from Nut/OS 4.2.1 to
>>>>
>>> 4.4.1
>>>
>>>> and some of my time-out calculations did not work anymore as they were
>>>>
>>> too
>>>
>>>> short.
>>>>
>>>>
>>>> Current implementation:
>>>> -----------------------
>>>>
>>>> u_long NutTimerMillisToTicks(u_long ms)
>>>> {
>>>> u_long x;
>>>>
>>>> x = ms * NutGetTickClock() / 1000UL;
>>>> if (x == 0) {
>>>> x = 1;
>>>> }
>>>>
>>>> return (x);
>>>> }
>>>>
>>>> [..snip..]
>>>>
>>>> Suggested implementation:
>>>> -------------------------
>>>> u_long NutTimerMillisToTicks(u_long ms)
>>>> {
>>>> return (ms * NutGetTickClock() + 500) / 1000UL;
>>>> }
>>>>
>>>>
>>> _______________________________________________
>>> http://lists.egnite.de/mailman/listinfo/en-nut-discussion
>>>
>> _______________________________________________
>> http://lists.egnite.de/mailman/listinfo/en-nut-discussion
>>
>
> _______________________________________________
> http://lists.egnite.de/mailman/listinfo/en-nut-discussion
>
>
More information about the En-Nut-Discussion
mailing list