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

How to program sbits which can be indexed ?

I would like to to use the bit addressable memory range (bdata) with an index variable as in the following example:

#define SETBIT(I) WAIT_4_MUTEX; flags^I=1; FREE_MUTEX;

unsigned char bdata flags;

func () {
unsigned char i;
  for (i=0; i < MAX_CELL; i++) {
    // do interesting stuff
    SETBIT(i);
  }
}

This construction of coarse gives a lval error on the use of the SETBIT macro.

My current fall back are the macro's:

#define SETBIT(I) base |= ((unsigned char) (1 << I))
#define CLEARBIT(I) base &= ~((unsigned char) (1 << I))

But still have the feeling that I am wasting cycles on the shift operators in these two macro's and because this is within a mutex region the timing might become an issue.

Is there a better way to set/reset the i-th bit of a bit addressable variable ?

Parents
  • Is there a better way to set/reset the i-th bit of a bit addressable variable ?

    Using a switch/case structure would avoid the shift by a variable.

    void set_bit(unsigned char idx)
    {
       switch(idx)
       {
       case 0:
          bit_0 = 1;
          break;
       case 1:
          bit_1 = 1;
          break;
       case 2:
          bit_2 = 1;
          break;
       [...]
       }
    }
    
    
    

Reply
  • Is there a better way to set/reset the i-th bit of a bit addressable variable ?

    Using a switch/case structure would avoid the shift by a variable.

    void set_bit(unsigned char idx)
    {
       switch(idx)
       {
       case 0:
          bit_0 = 1;
          break;
       case 1:
          bit_1 = 1;
          break;
       case 2:
          bit_2 = 1;
          break;
       [...]
       }
    }
    
    
    

Children
  • Using a switch/case structure would avoid the shift by a variable. but be excruciatingly slow.

    the only approaches that have any reasonable throughput is the "mask in the call" and the lookup table.

    Erik

  • The switch statement can be fast for some processors, that have efficient methods to call a jump table. But such processors normally also have a barrel shifter in which case they can shift n steps in constant time.

    Easiest is if the caller can supply a constant value to or in, or the mask for clearing.

  • The switch statement can be fast for some processors, that have efficient methods to call a jump table.

    JMP @A+DPTR ?

    And if the compiler isn't clever enough to translate the switch/case structure this way, there is still the option of writing an assembly function.

    This approach also works for more than 8 bits without any further effort, while the lookup table approach will need additional decision steps in such cases.

  • Not necessarily.

    I think C51 can do it quite well with a jump table...

  • Only a very bad C compiler would not consider converting a switch() into a jump table if the case statements forms a suitably compact group.

    One problem here is how much setup code that will be needed, where the compiler first validates if the shift value is within the range covered by the individual case statements. If the shift count is signed, the compiler will have to test if the value is < 0 or > 7 before knowing of the jump table can be used. In this special case, you may possibly be able to help the compiler with a switch (n&7) to force the count within the processed range of the switch statement.

    For a processor with barrel shifter, the compiler vendor can make it easy on themselves by not testing the range of n before performing the shift, and just document that the shift operation is undefined if n is outside range. Such optimization is never allowed when the compiler generates code for a jump table, since and out-of-bounds value would result into a random jump.

  • Only a very bad C compiler would not consider converting a switch() into a jump table if the case statements forms a suitably compact group.

    in that case Keil is "a very bad C compiler" if you want to debug (no code overlay optimization) and a decent one if you do not care about using your ICE.

    I have a strong suspicion that Keil has located some things in the optimizer that should be in the compiler, just to make the optimizer more impressive.

    Erik

  • Several optimizing steps are part of the compiler and are run before the code generation. The optimization is performed on a statement/expression tree.

    Some optimization steps are done after code has been generated.

    With optimization turned off, the compiler will basicaly generate code for one statement at a time, since you have more or less requested that it shouldn't look at - and react to - statements before/after.

    If you do generate the code one statement at a time and then just back-patch jump instruction targets, you will get code that is very easy to debug, but can be extremely inefficient. That isn't a fault with the compiler. Just the result of the developer asking for assembler code that has a 1:1 mapping with the source.

    If the processor don't have a feature to process one single instruction and then return into debug mode, then the debugger must figure out beforehand which alternative in a jump table that will be taken, so it can insert a breakpoint at that specific target address - few flash-based processors would be capable of adding breakpoints to every possible case statement. Maybe Keil has decided that they don't want to use jump tables when all optimization is turned off.

    I don't spend much time with debuggers, so I normally don't care about the generated code being harder to debug. Hence, I normally always use optimization just to make sure that all my code testing will be performed with the same optimization level I plan to ship with. In some situations, the developer don't have an option - the size difference between no optimization and optimize-for-size may be the difference between failed linking and a working application with space available for some future extensions.

    It is very seldom good compilers produces broken code when otpimization is turned on, so if a program doesn't work as expected, it will normally be because of a direct bug in the source code, or a timing issue. Both can normally be located and solved reasonably fast by reading the source code, and maybe the addition of a debug printout or toggling an output pin around critical code sections.

    Most times when people think they have found a compiler optimization bug, they have turned on an optimization step where they ask the compiler to ignore potential aliasing problems, and have then written code where the same variable may be accessed using multiple access paths - for example updating a global variable using both direct access and through a pointer.

  • there would be none, zero, nada issues with ICE debugging if the compiler used a jmp @. I can not understand why you need to use the optimizer (with the other ill effects) in order to have an efficient switch ststement.

    Erik