Learn how easy it is to sync an existing GitHub or Google Code repo to a SourceForge project! See Demo

Close

Why these 51 codes does not work?

ss77
2007-04-14
2013-03-12
  • ss77
    ss77
    2007-04-14

    Wrote a simple program below trying out timer2 incrementing countms in the ISR.
    (1) I need to use "diff = countms - oldCountms" in order for the if statement to work as below.
    (2) However, if I do "if ((countms - oldCountms) >= (unsigned char) 200)" directly, it refuses to work.

    (1) uses an extra variable "diff". Looking at the codes generated for it uses 8-bit subtraction. It works fine.

    On the other hand, for (2), it generates 16-bit subtraction and unfortunately, it does not work. I used (unsigned char) casting for 200 hoping that it would be forced to use 8-bit comparison but was not successful.

    Not sure if this is because I did something wrong. Really appreciate any help as I feel that it is not logical that a separate variable must be used to ensure that it works.

    Thanks,
    SS77
    -----------------------------
    #include <c8051F330.h>
    #include "f330.h"

    void Init_Device(void);

    volatile unsigned char countms;

    void tmr2ISR ( void ) interrupt 5 using 1 {
        TF2 = 0;
        countms++;
    }

    void main ( void ) {
        unsigned char count200ms,oldCountms,diff;

        countms = oldCountms = count200ms = 0;
        Init_Device();
        TI = 1;
        EA = 1;

        putStr("Testing\r\n");

        while(1) {
            diff = countms - oldCountms;
            if (diff >= 200) {
    //        if ((countms - oldCountms) >= (unsigned char) 200) {
                putCh('t');
                oldCountms += 200;
                count200ms++;
                if (count200ms == 5) {
                    // 5 x 200ms = 1 sec
                    putCh('A');
                    count200ms = 0;
                }
            }
        }
    }

     
    • Maarten Brock
      Maarten Brock
      2007-04-14

      It does not help to cast 200 to an unsigned char. Instead you could cast the expression (countms - oldCountms), because this expression is promoted to int according to the rules. After the first oldCountms += 200 this is always true: -200 <= (countms - oldCountms) <= 55.

      It might be better to set countms back by 200 and you can get rid of diff and oldCountms. But you must do it in a critical block to make the operation atomic.

      critical { countms -= 200; }

       
    • ss77
      ss77
      2007-04-14

      Thanks for pointing out. After casting as below it does work. It now uses 8-bit subtract ...
      if (((unsigned char)(countms - oldCountms)) >= 200) {

      Did an experiment: When I tried casting to unsigned int this time, it failed to work:
      if (((unsigned int)(countms - oldCountms)) >= 200) {
      Looks like it becomes always true as it does not seem to display 'A' at 1 sec interval.

      I then tried statement below again (without casting) and compile using Keil C (I am using Silabs IDE which allows me to switch tool chain easily) and it works fine. I feel that this statement is much more intuitive from programmer point of view.
      if ((countms - oldCountms) >= 200) {

      Does it mean that Keil C does not follow the C implementation rule strictly?

      Regarding setting back countms by 200, I assume that the main function may not have time (when I add in more codes into the main loop) to check countms regular enough, thus the difference (countms - oldCountms) may actually exceed 200. In this case, countms is meant to be freely running counter incrementing at 1ms interval similar to the Programmable Counter Array operating in Compare mode by keep adding 200 to oldCountms.

      Thanks so much for pointing out. Looks like I need to be very careful with situation above when I use SDCC.

       
    • Maarten Brock
      Maarten Brock
      2007-04-16

      (((unsigned int)(countms - oldCountms)) >= 200)

      equals

      (((unsigned int)((int)countms - (int)oldCountms)) >= (unsigned int)200)

      which is not true while oldCountms is still 0 and countms < 200. When countms reaches 200 it becomes true and oldCountms is assigned 200. After that, while countms - oldCountms <= 55, the comparison is not true. When countms overflows to 0 the casted result is >= 65336 and the comparison stays true for a long time. Adding 200 to oldCountms each time does not help because it overflows back to something <256.

      If the original code works fine using Keil it means they do not strictly follow the rules. I can only hope they documented that clearly.

      What you feel is more intuitively does not really matter. C has been standardized and we must follow the rules. Software must do as you write, not what you intend.

      Setting back countms is no different from incrementing oldCountms, it can still exceed 200. But remember there is little room left as countms overflows after 255.

      And in my opinion you should be very careful when using Keil instead of when using SDCC in this situation.

      Maarten

       
    • ss77
      ss77
      2007-04-16

      Thanks for taking time to explain exactly what is going on. So they are promoted before subtraction! Now I understand better. My intention was to "ignore" countms overflowing so that 8-bit subtraction (ignoring any borrow) would still give "valid" result. (An idea I learned from PCA - it puzzled me when I first learned.) If I use this approach in a "very simple multitasking environment", each task can have its own oldCountms and all would work fine with reference to the free running shared countms. Example the following would give delay of 50ms in a task:
          oldCntms = countms;    // take current countms as reference
          if ((countms - oldCntms) < 50) {
              defer(); // waiting for 50ms to pass, switch another task
          }
          // about 50 ms has passed

      Checked out the assembly codes generated by Keil C. It uses 8-bit subtraction without any promotion and compared with 8-bit version of 200.
      if ((countms - oldCountms) >= 200) {
          CLR      C
          MOV      A,countms
          SUBB     A,oldCountms?141    ; 8-bit subtraction
          CLR      C
          SUBB     A,#0C8H    ; 8-bit compare with 200 (0xc8)
          JC       ?C0002

      When I tried this (which actually does not make sense as both countms and oldCountms are 8-bit unsigned) and yet the value compared 300 is > 8-bit (I was expecting a warning but there were none on this). The code generated now uses 16-bit for all operations (including promotions):
      if ((countms - oldCountms) >= 300) {
          MOV      R5,countms
          CLR      C
          MOV      A,R5
          SUBB     A,oldCountms?141
          MOV      R7,A
          CLR      A
          SUBB     A,#00H
          MOV      R6,A    ; 16-bit results in R6R7
          CLR      C
          MOV      A,R7    ; 16-bit compare with 300 (0x012C)
          SUBB     A,#02CH
          MOV      A,R6
          XRL      A,#080H
          SUBB     A,#081H
          JC       ?C0002

      It looks like Keil C has been targeting the intention of the programmer. (Though the later C statement does not make sense.)

      I suppose for the intention that I had, it would be fine if both countms and oldCountms are declared at 16-bit unsigned int and any overflow would not matter as it won't be further promoted beyond 16-bit during the operations. In this case, I suppose I would need to take care of non-atomic 16-bit operation.

      Just expressing my personal opinion: I feel that rules (including standards) adopted should accommodate reasonable intentions of human programmers, though it may end up more complicated than straight forward ones. (It is worth it. Just a personal opinion.)

      For my situation above, actually it is a bit technical assuming understanding of binary system well. I did not expected the behaviour, thus posted it in the forum.

      Thanks for your answers. It has really given me much better understanding of what is going on.

      Just sharing my background. I am teaching students using 8051-based system. We have been using evaluation version of Keil for almost 5 years. It is a great tool. However, it has been a serious problem when my project students want to move programs into flash memory and also very often they go beyond 2K. When we discovered Silabs chips + the toolStick + Silab IDE + SDCC, we felt so empowered. The codes generated by SDCC are good. We are committed to adopt SDCC for our student training.

      Thank to the SDCC team.

       
      • > However, it has been a serious problem when my
        > project students want to move programs into flash
        > memory and also very often they go beyond 2K.

        I have no intention to change your commitment to SDCC;)
        just want to point out that in your case the size limit
        of the evaluation version of Keil might be slightly less
        stringent.

        The Keil C compiler shipped with the Silabs Kit has a
        limit of 4 kByte (instead of the 2 kByte code size limit
        for Keil's download evaluation versions).
        So you probably own one license for one 4 kByte limited
        Keil evaluation version per Silabs Kit.

        Greetings,
        Frieder

         
    • Maarten Brock
      Maarten Brock
      2007-04-17

      You're welcome,

      We try to adhere to the standard if possible even when it feels like unintended behaviour esp. for 8 bit mcu's. We did not write the C standard and are in no position to change it, but we also do not want to invent our own deviation from it.

       
    • wek
      wek
      2007-04-17

      Couldn't this be covered by some sort of warnings - like "uchar cast to int"?

      Maybe warning is not the best form for it, but some compilers do provide this sort of "hinting", especially valuable for novices like me. For example, I'd also like to have a warning on things line "if (a=b)"...

      Jan Waclawek

       
    • ss77
      ss77
      2007-04-25

      We are not intending to use the Silabs development kit (DK). We only own one unit of the DK which comes with 4K limit version. We intend to use the ToolStick base adapter providing C2 interface to our target processor. We have many units of it now. [We are really impressed by the such simple C2 interface providing almost the equivalence of traditional in-circuit emulation.]

      Just tested on my machine that is installed with recent tools from Silabs for ToolStick.
      It only allows about 2K code size.

      Thanks,
      Song Sing