cancel
Showing results for 
Search instead for 
Did you mean: 

Compiler and bitfields

lseletron
Associate II
Posted on March 26, 2007 at 18:05

Compiler and bitfields

10 REPLIES 10
lseletron
Associate II
Posted on March 20, 2007 at 23:56

Hi,

I added the following declaration in st72325.h:

typedef struct

{

unsigned char BIT_0:1;

unsigned char BIT_1:1;

unsigned char BIT_2:1;

unsigned char BIT_3:1;

unsigned char BIT_4:1;

unsigned char BIT_5:1;

unsigned char BIT_6:1;

unsigned char BIT_7:1;

} REGISTER_BITS;

Further in the st72325.h where port A is declared I added another declaration as well:

STVD7_EXTERN volatile unsigned char PADR @0x00; // ST code

STVD7_EXTERN volatile REGISTER_BITS PADR_BITS @0x00; // My code

Having done this, now I can write code like this:

PADR_BITS.BIT_4=1;

which is (for me) more convenient and clearer than OR-ing or using Set/ClearBit macros.

But I have a problem when I write this:

while(1)

{

PADR_BITS.BIT_4=!(PADR_BITS.BIT_4);

}

The problem manifests itself in other pin in the same port behaving erratically. I solve the problem by casting the bit field or rewriting the operation:

while(1)

{

PADR_BITS.BIT_4=!((unsigned char)(PADR_BITS.BIT_4)); // works OK

//PADR_BITS.BIT_4=1-(PADR_BITS.BIT_4)); // works OK

}

Should not the compiler cast the bit field automatically or I have to do it manually whenever I access a register in this manner?

Also, as shown above, the members of the structure are called BIT_x.

If I define a macro like this:

#define BIT_0 0 // Same name like the member

the compiler complains. Why?

Thanks

wolfgang2399
Associate II
Posted on March 21, 2007 at 05:06

Hi zsavov,

often it helps to have a look at the compiled code. Please open the list-file and analyse the resulting assembler code.

My compiler does it all correct as you can see below. Only the source code

PADR.bit_4 = 1-PADR.bit_4;

gives an intricate code.

O.k.:

; PADR.bit_4 = !PADR.bit_4;

LD A,PADR

XOR A,#16

LD PADR,A

Also o.k.:

; PADR.bit_4 = !(PADR.bit_4);

LD A,PADR

XOR A,#16

LD PADR,A

If the code is allrigth the problem might be with an interrupt, working on your PADR register. You can only see that phenomena at the assembler code. I'll try to explain the spurious effect:

:::

LD A,PADR

XOR A,#16

_____interrupt_____

 

:::

 

LD PADR,A ; PADR modified by interrupt

 

:::

___end of interrupt___

LD PADR,A

:::

As you can see above, the manipulation of your interrupt routine is lost in this case but you will never find the fault in your C-code.

Best regards

WoRo

luca239955_st
Associate III
Posted on March 22, 2007 at 05:49

what compiler are you using?

If it's Cosmic, the suggested syntax is (example)

Code:

volatile char TESTREG @0x34;

_Bool TESTREG_7 @TESTREG:7;

that will allow you to use the bit directly and the compiler will produce compact code. Example:

Code:

600 ; 123 if (TESTREG_7==1) {TESTREG_7=0;}

602 1073 0f3402 btjf _TESTREG_7,L722

605 1076 1f34 bres _TESTREG_7

606 1078 L722:

607 ; 124 TESTREG_7=1;

609 1078 1e34 bset _TESTREG_7

610 ; 125 TESTREG_7=0;

612 107a 1f34 bres _TESTREG_7

Regards,

Luca (Cosmic)

lseletron
Associate II
Posted on March 23, 2007 at 03:29

Hi,

Thank you for your replies!

I use Cosmic latest version.

I had a look at the assembly code. This is how it looks in the two cases:

// erroneous behaviour

2065 ; 117 PADR_BITS.BIT_4=!((PADR_BITS.BIT_4));

2067 000a b600 ld a,_PADR_BITS

2068 000c a810 xor a,#16

2069 000e b700 ld _PADR_BITS,a

// OK

2065 ; 117 PADR_BITS.BIT_4=!((unsigned char)(PADR_BITS.BIT_4));

2067 000a 4f clr a

2068 btst _PADR_BITS,#4

2069 000e 49 rlc a

2070 000f 4d tnz a

2071 0010 2702 jreq L21

2072 0012 2004 jp L6

2073 0014 L21:

2074 0014 1800 bset _PADR_BITS,#4

2075 0016 2002 jra L01

2076 0018 L6:

2077 0018 1900 bres _PADR_BITS,#4

2078 001a L01:

The code in the second case seems more elaborated.

It seems it happens as Mr WoRo suggests.

In the erroneous case setting/reseting the bit in the port is done by operating on the whole port and while the port register value is transferred to/from Acc, another bit of the port gets changed by an interrupt and this change is overwritten by the ld/xor operation still going on.

In the code which works OK, setting /reseting the bit is done on the bit individually rather than on the whole port, which means that if another bit in the port is manipulated in an interrupt, the change is not lost.

Funny how a simple cast changes it all.

BTW, there is an assembler instruction ''btst''. I could not find it in the data sheet.I take it as bit test and put result in A.

thanks again

wolfgang2399
Associate II
Posted on March 23, 2007 at 04:53

Hi zsavov,

as _luca described in his posting of 2007-03-22 you may force the compiler to generate a more effective code when replacing your code with

if(PADR_BITS.BIT_4 == 1) PADR_BITS.BIT_4 = 0;

else PADR_BITS.BIT_4 = 1;

I think it's the way to get the most effective code while using bset/bclr instructions.

Notice: It only works with bitfields which you can address in short 8-bit addressing mode!!!

_luca: I suppose there may be a fault in your code??

Regards

WoRo

luca239955_st
Associate III
Posted on March 23, 2007 at 09:54

@zsavov: btst is an assembler macro defined in the file macro.st7 of your compiler installation

@woro: I did not test that code (just made up a quick example), but I see no problem in it...

Luca

lseletron
Associate II
Posted on March 24, 2007 at 08:56

Hi,

Still, it is not clear to me why when the (unsigned char) cast is applied everything is OK, whereas without the cast we have the very simple but error-prone code. Also, how do I know if the compiler has done the right thing when I manipulate individual bits in a port (possibly from interrupts)?

The problem is not as much which code is the most effective one, it is rather how do I know that this will not happen.

Thank you

wolfgang2399
Associate II
Posted on March 26, 2007 at 09:05

wolfgang2399
Associate II
Posted on March 26, 2007 at 09:09

Sorry there seams to be a problem with the ST message handler using the CODE feature. So i'll try again:

----------------------------------------------------------------------

Hi zsavov,

I feel certain that you must check the resulting assembler code if there are critical applications or you don't find the reason for a mysterious malfunction.

There is no rule in ANSI-C how to compile source code to assembler code. The code you get depends on whether you optimise to speed or to program space, whether you use the volatile switch or not, whether you chose short or long memory model or stack model, whether you take compiler xx or yy or zz.

E.g. my (Hiware-)compiler generates always the same code with

- PADR_BITS.BIT_4=!PADR_BITS.BIT_4;

- PADR_BITS.BIT_4=!(PADR_BITS.BIT_4);

- PADR_BITS.BIT_4=!((PADR_BITS.BIT_4));

- PADR_BITS.BIT_4=!((unsigned char)(PADR_BITS.BIT_4));

namely:

LD A,PADR

XOR A,#16

LD PADR,A

only with

- PADR_BITS.BIT_4 = 1-PADR_BITS.BIT_4;

I'll get

189: PADR_BITS.BIT_4 = 1-PADR_BITS.BIT_4;

0019 080003 BTJT PADR_BITS,#4,*6 ;abs = 001f

001c 4f CLR A

001d 2002 JRT *4 ;abs = 0021

001f a601 LD A,#1

0021 b700 LD _spill_0,A

0023 a601 LD A,#1

0025 b000 SUB A,_spill_0

0027 a501 BCP A,#1

0029 2604 JRNE *6 ;abs = 002f

002b 1900 BRES PADR_BITS,#4

002d 2002 JRT *4 ;abs = 0031

002f 1800 BSET PADR_BITS,#4

The code above is dreadfully intricate but: it would work correctly in your application.

Regards

WoRo

- _luca: As I interpret your code, bit4 won't be toggled but will always be 0 at the end of your code.