This discussion has been locked.
You can no longer post new replies to this discussion. If you have a question you can start a new discussion

Timer, t1-t0, integral promotion

For ARM7, A hardware timer is 32-bits, and unsigned int is 32-bits too;
For a 16-bits MCU, A hardware timer is 16-bits, and unsigned int is 16-bits too;

So,

t0 = timer_val;
while ( ( timer_val - t0 ) < delay_val );

provide a simple and good delay;

Not so sure about, if a cast is needed.

t0 = timer_val;
while ( (unsigned int)( timer_val - t0 ) < delay_val );

Just noticed that, due to the integral promotion,

unsigned short t0, t1;
( t1 - t0 ) is a signed int.

It seems that,

unsigned int t0, t1;
( t1 - t0 ) is still an unsigned int.

I noticed this, because I use unsigned short to present a 16-bits timer_val on my X86-PC for testing/simulating purpose, and the result is not what I expected.

  • Does the below code still work?

    void delay(uint32_t delay)
    {
        int32_t t0;
        t0 = (int32_t)TIMER_VALUE; /* assuming the timer is 32-bit */
        while ( ( (int32_t)TIMER_VALUE - t0 ) < delay );
    }
    

    signed arithmetic overflows cleanly

  • I am using my stupid way to try to figure out the key point.

    #include <stdio.h>
    #include <stdint.h>
    
    
    void main(void)
    {
            int32_t now = -2147483648, end;
            end = now - 1;
            // while (now - end < 0);
            printf("now = %d\n", now);
            printf("end = %d\n", end);
    
            if ( (int32_t)(now - end) < 0 )
                    printf("Less than 0\n");
            else
                    printf("It is >= Zero\n");
    
            system("PAUSE");
    }
    

    now = -2147483648
    end = 2147483647
    It is >= Zero

    #include <stdio.h>
    #include <stdint.h>
    
    
    void main(void)
    {
            int32_t now = -2147483648, end;
            end = now - 1;
            // while (now - end < 0);
            printf("now = %d\n", now);
            printf("end = %d\n", end);
    
            if ( (now - end) < 0 )
                    printf("Less than 0\n");
            else
                    printf("It is >= Zero\n");
    
            system("PAUSE");
    }
    

    now = -2147483648
    end = 2147483647
    It is >= Zero

    It is the same.

  • #include <stdio.h>
    #include <stdint.h>
    
    
    void main(void)
    {
            int16_t now = -32768, end;
            end = now - 1;
            // while (now - end < 0);
            printf("now = %d\n", now);
            printf("end = %d\n", end);
    
            if ( (int16_t)(now - end) < 0 )
                    printf("Less than 0\n");
            else
                    printf("It is >= Zero\n");
    
            system("PAUSE");
    }
    

    now = -32768
    end = 32767
    It is >= Zero

    #include <stdio.h>
    #include <stdint.h>
    
    
    void main(void)
    {
            int16_t now = -32768, end;
            end = now - 1;
            // while (now - end < 0);
            printf("now = %d\n", now);
            printf("end = %d\n", end);
    
            if ( (now - end) < 0 )
                    printf("Less than 0\n");
            else
                    printf("It is >= Zero\n");
    
            system("PAUSE");
    }
    

    now = -32768
    end = 32767
    Less than 0

    It is different.

  • Does the below code still work?

    As far as I can see, it should. There are some subtle points in this code.
    1) TIMER_VALUE is converted from uint32_t to int32_t. We are assuming two's complement arithmetic.
    2) Two values of type int32_t are subtracted with (likely) an overflow. Again, we are assuming two's complement arithmetic, so the result really is the number of ticks elapsed.
    3) According to integer promotion rules, the result of subtraction is converted to uint32_t prior to comparison. That doesn't change anything since we know that the result is positive anyway.

    I wonder if the same can be done with fewer type conversions:

    void delay(uint32_t delay)
    {
        uint32_t t0;
        t0 = TIMER_VALUE; /* assuming the timer is 32-bit */
        while ( ( TIMER_VALUE - t0 ) < delay );
    }
    

    Since we are assuming two's complement arithmetic anyway, we might as well get rid of conversion to signed integer. So points 1) and 3) above no longer apply. Going to signed integer was plain stupidity on my part, I suppose.

    By the way, here is how lwip computes the difference between two timer values:

    /** Get the absolute difference between 2 u32_t values (correcting overflows)
     * 'a' is expected to be 'higher' (without overflow) than 'b'. */
    #define LWIP_U32_DIFF(a, b) (((a) >= (b)) ? ((a) - (b)) : (((a) + ((b) ^ 0xFFFFFFFF) + 1)))
    


    I beleive this code does not depend on two's complement arithmetic and it avoids subtraction with overflow. A smart compiler should simply replace the macro with ((a) - (b)) where it is safe to do so.

  • It is different.

    No wonder.
    When you are doing ( (now - end) < 0 ), the 16-bit signed integers are promoted to 32-bit ones prior to subtraction, so an overflow is no longer possible. Hence, the result is negative.
    When you are doing ( (int16_t)(now - end) < 0 ), you are converting the result of subtruction (which is -65535 or 0xFFFF0001) to int16_t. The conversion simply throws away the upper 16 bits, so you are left with 0x0001 which is positive.

  • @John Linq,

    You are _not_ stupid and you are certainly _not_ an idiot !

  • #include <stdio.h>
    #include <stdint.h>
    
    
    void main(void)
    {
            uint32_t t0 = 4294967290, t1 = 19;    // t1 < t0
    
            printf("t0 u = %u\n", t0);
            printf("t1 u = %u\n", t1);
            printf("t0 d = %d\n", t0);
            printf("t1 d = %d\n", t1);
    
            printf("result u = %u\n", (t1 - t0));
            printf("result d = %d\n", (t1 - t0));
    
            if ( (t1 - t0) == 25 )
                    printf("OK\n");
            else
                    printf("NG\n");
    
            system("PAUSE");
    }
    


    t0 u = 4294967290
    t1 u = 19
    t0 d = -6
    t1 d = 19
    result u = 25
    result d = 25
    OK

    #include <stdio.h>
    #include <stdint.h>
    
    
    void main(void)
    {
            int32_t t0 = 2147483640, t1 = -2147483631;    // t1 < t0
    
            printf("t0 u = %u\n", t0);
            printf("t1 u = %u\n", t1);
            printf("t0 d = %d\n", t0);
            printf("t1 d = %d\n", t1);
    
            printf("result u = %u\n", (t1 - t0));
            printf("result d = %d\n", (t1 - t0));
    
            if ( (t1 - t0) == 25 )
                    printf("OK\n");
            else
                    printf("NG\n");
    
            system("PAUSE");
    }
    


    t0 u = 2147483640
    t1 u = 2147483665
    t0 d = 2147483640
    t1 d = -2147483631
    result u = 25
    result d = 25
    OK

  • For int32_t/uint32_t, the result is what I want.
    Due to the integral promotion, the result of int16_t/uint16_t is not I want.

  • My mistake:

    #include <stdio.h>
    #include <stdint.h>
    
    
    void main(void)
    {
            int32_t t0 = 2147483640, t1 = 123;    // t1 < t0
    
            printf("t0 u = %u\n", t0);
            printf("t1 u = %u\n", t1);
            printf("t0 d = %d\n", t0);
            printf("t1 d = %d\n", t1);
    
            printf("result u = %u\n", (t1 - t0));
            printf("result d = %d\n", (t1 - t0));
    
            system("PAUSE");
    }
    


    t0 u = 2147483640
    t1 u = 123
    t0 d = 2147483640
    t1 d = 123
    result u = 2147483779
    result d = -2147483517

    #include <stdio.h>
    #include <stdint.h>
    
    
    void main(void)
    {
            uint32_t t0 = 2147483640, t1 = 123;    // t1 < t0
    
            printf("t0 u = %u\n", t0);
            printf("t1 u = %u\n", t1);
            printf("t0 d = %d\n", t0);
            printf("t1 d = %d\n", t1);
    
            printf("result u = %u\n", (t1 - t0));
            printf("result d = %d\n", (t1 - t0));
    
            system("PAUSE");
    }
    


    t0 u = 2147483640
    t1 u = 123
    t0 d = 2147483640
    t1 d = 123
    result u = 2147483779
    result d = -2147483517

  • My mistake again:

    #include <stdio.h>
    #include <stdint.h>
    
    
    void main(void)
    {
            int32_t t0 = 2147483640, t1 = 123;    // t1 < t0
    
            printf("t0 u = %u\n", t0);
            printf("t1 u = %u\n", t1);
            printf("t0 d = %d\n", t0);
            printf("t1 d = %d\n", t1);
    
            printf("result u = %u\n", (t1 - t0));
            printf("result d = %d\n", (t1 - t0));
    
            if ( (t1 - t0) == 2147483779 )
                printf("OK\n");
            else
                printf("NG\n");
    
            system("PAUSE");
    }
    

    t0 u = 2147483640
    t1 u = 123
    t0 d = 2147483640
    t1 d = 123
    result u = 2147483779
    result d = -2147483517
    OK

    It is still OK!!!

  • I think this topic is too difficult to understand for me.

    Many thanks to everyone involved.

    My favorite:

    /* both t0 and timer_val are unsigned */
    t0 = timer_val;
    while ( (cast to timer-size unsigned)( timer_val - t0 ) < delay_val );
    

  • Isn't it, for computer science, both signed arithmetic and unsigned arithmetic are based on two-complement arithmetic?

    Indeed it isn't.

    Twos-complement is a representation technique for negative numbers. As such, it has absolutlely no relevance to unsigned arithmetic.

  • By the way, here is how lwip computes the difference between two timer values:

    /** Get the absolute difference between 2 u32_t values (correcting overflows)
     * 'a' is expected to be 'higher' (without overflow) than 'b'. */
    #define LWIP_U32_DIFF(a, b) (((a) >= (b)) ? ((a) - (b)) : (((a) + ((b) ^ 0xFFFFFFFF) + 1)))
    

    That macro is a spectacular waste of effort, based on the completely false assumption that unsigned arithmetic had any overflows that would need "correcting". Oh, and the comment is wrong, too: that macro computes the difference, not the absolute difference. The latter would be something like (a >= b)?(a - b):(b - a)

    So let's dig in:

    b & 0xFFFFFFFF
    

    is exactly equivalent to

    0xFFFFFFFF - b
    

    which makes the term to the right of the ':' operator:

    a + (0xFFFFFFFF - b) + 1
    


    which in term is exactly the same as:

    a - b + (0x100000000)
    


    but since we're assuming 32-bit unsigned integers, which wrap around modulo 0x100000000, that's nothing else but

    a - b
    

    There are three cases you can end up in if you use this:

    1) it's identical to a much simpler

    #define LWIP_U32_DIFF(a,b) ((a)-(b))
    

    2) u32_t is not actually a 32-bit integer type. In that case the macro doesn't work any better than the simpler version from case 1) would.

    3) something happens that you pretty definitely didn't want to. Imagine what will happen if you call this as LWIP_U32_DIFF(a++, TIMER_REGISTER), and see what multiple evaluation of those arguments will do your result.

  • #define LWIP_U32_DIFF(a, b) (((a) >= (b)) ? ((a) - (b)) : (((a) + ((b) ^ 0xFFFFFFFF) + 1)))
    

    Sorry for my limited technical ability, I am very unfamiliar with this calculating and proving.

    b ^ 0xFFFFFFFF
    

    Is it equivalent to

    b & 0xFFFFFFFF
    

  • So, I guess I got it.

    b ^ 0xFFFFFFFF
    

    is exactly equivalent to

    0xFFFFFFFF - b