Help needed in dereferencing a union

J

jlara

Working on an embedded controller, I generally find it convenient to
define the following de-referenced pointer:

#define portb (*(unsigned char *)(0x03u))

This enable me to write to a port as follows:

portb = 0x0f;

But when I want to define a register as follows, the code does not
compile:

typedef union
{
uint8 byte;

struct
{
uint8 : 4;
uint8 tffca : 1;
uint8 tsfrz : 1;
uint8 tswai : 1;
uint8 ten : 1; /* timer enable */
}bit;

}tSCR1;

#define scr1 (*(tSCR1 *)(0x4c))

useage:

scr1.bit.ten = 1; /* the compiler complains about this */

I find it very convenient to use this scheme if possible. It is very
clear what is being manipulated just by reading the code. I prefer
this over: "scr1 = 0x01;". Is it possible to dereference this union
in the same fashion as the first example? Thanks very much in advance.
 
J

Jirka Klaue

typedef union
{
uint8 byte;
struct
{
uint8 : 4;

uint8 bla : 4;
uint8 tffca : 1;
uint8 tsfrz : 1;
uint8 tswai : 1;
uint8 ten : 1; /* timer enable */
}bit;
}tSCR1;

#define scr1 (*(tSCR1 *)(0x4c))
scr1.bit.ten = 1; /* the compiler complains about this */

No, it complains about a missing field name in bit.

Jirka
 
J

Jens.Toerring

uint8 bla : 4;
No, it complains about a missing field name in bit.

It's not the missing field name my compiler (gcc) is complaining
about (unnamed bit-fields being allowed by the standard) but the
type of the variable used for the bit-field - "unit8" isn't a
standard type, it would have to "uint8_t" (assuming a C99 compli-
ant compiler) and then for bit-fields only signed and unsigned
ints can be used portably (and _Bool in C99) according to the
standard. With that out of the way it compiles without problems,
the line indicated by the OP getting accepted without complaints.

If the OPs compiler has a real problems with only that line (which
I guess shouldn't be the case) an alternative would be to try to
use a pointer to the union instead

#defined scr1 ( ( tSCR1 * ) 0x4c )

and then do

scr1->bit.ten = 1;

or to defined macros for setting an resetting the bits, i.e.

#define TIMER_ENABLE() do { ((tSCR1 *) 0x4c )->bit.ten = 1; } while(0)
#define TIMER_DISABLE() do { ((tSCR1 *) 0x4c )->bit.ten = 0; } while(0)

which, since this seems to be mostly about readability of the code,
could result in the easiest to read code.

Regards, Jens
 
O

Old Wolf

jlara said:
the code does not compile:

typedef union
{
uint8 byte;

struct
{
uint8 : 4;
uint8 tffca : 1;
uint8 tsfrz : 1;
uint8 tswai : 1;
uint8 ten : 1; /* timer enable */
}bit;

}tSCR1;

#define scr1 (*(tSCR1 *)(0x4c))

scr1.bit.ten = 1; /* the compiler complains about this */

The code looks correct to me (assuming that uint8 is
unsigned char, and that your compiler supports uint8 as a
bitfield type).

What is the exact compiler error?

Another thing to try would be to use other names than 'bit'
and 'ten', in case those symbols are #defined macros. One
compiler I use has #define bit <something> in its standard
headers.
 
J

jlara

You are absolutely correct. uint8 is defined as unsigned char. The
compiler I am using is the GNU compiler.

I may have an issue with where the header files are included. I am
still looking into that. Many thanks.
 
J

jlara

I got it to compile successufully with the following macro:

#define tios (*(tTIOS volatile *)(C_REG_BASE_ADDR + 0x040u))

but the following does not work:

#define tios *(tTIOS volatile *)(C_REG_BASE_ADDR + 0x040u)

I am using the GNU gcc compiler version 3.0.4. Does anyone have any
idea why the difference? Thanks in advance.
 
K

Keith Thompson

jlara said:
I got it to compile successufully with the following macro:

#define tios (*(tTIOS volatile *)(C_REG_BASE_ADDR + 0x040u))

but the following does not work:

#define tios *(tTIOS volatile *)(C_REG_BASE_ADDR + 0x040u)

I am using the GNU gcc compiler version 3.0.4. Does anyone have any
idea why the difference? Thanks in advance.

You got what to compile successfully? Please provide some context
when you post a followup; we don't necessarily have easy access to the
parent article. Google groups makes it unnecessarily difficult, but
not impossible, to do this properly.

If you've been reading this newsgroup, you should have seen the
following many times:

If you want to post a followup via groups.google.com, don't use
the broken "Reply" link at the bottom of the article. Click on
"show options" at the top of the article, then click on the
"Reply" at the bottom of the article headers.

Without seeing the context in which the macro is used, I can't tell
what the problem is, but in general macros that are intended to be
used as expressions should be fully parenthesized to avoid operator
precedence problems. The entire macro definition should be in
parentheses, and if it's a function-like macro, each argument
reference should be in parentheses.

Remember that macro expansion is purely textual, and the result is a
sequence of tokens, not necessarily an expression.

(Also, by convention macro names are generally in all-caps; TIOS would
be a better name than tios. You can use lower case if you're
deliberately hiding the fact that it's a macro, but that's rarely a
good idea.)

If you're a Douglas Adams fan (or even if you're not), the following
is a good illustration of the dangers of insufficient parentheses:

#include <stdio.h>

#define SIX 1+5
#define NINE 8+1

int main(void)
{
printf("%d * %d = %d\n", SIX, NINE, SIX * NINE);
return 0;
}
 
O

Old Wolf

jlara said:
I got it to compile successufully with the following macro:

#define tios (*(tTIOS volatile *)(C_REG_BASE_ADDR + 0x040u))

but the following does not work:

#define tios *(tTIOS volatile *)(C_REG_BASE_ADDR + 0x040u)

I am using the GNU gcc compiler version 3.0.4. Does anyone have any
idea why the difference? Thanks in advance.

What is tios? In your original message you wrote:

#define scr1 (*(tSCR1 *)(0x4c))

which is the correct form. Now you are telling me that it
does actually work and some different thing you never posted,
doesn't work?

In future, please post the exact code that you are having the
problem with, it will save a lot of time.

The reason: *a.b means *(a.b), whereas what you want is (*a).b
 
J

jlara

My sincere apologies. I was under the impression that the original
article and all of the threads were visible. I will recap all of the
information once more:

I am using the GNU gcc compiler for the Motorola 68hc1x
(http://www.gnu.org/software/m68hc11/):

gnu-68hc1x-2.2.exe (Release 2.2, Snapshot 20030501, Gcc 3.0.4 )

The compiler gives the error "request for member 'bit' in something not
a structure or union" for the following code:

/***************************************************

* TIOS : Timer input capture/output compare select
****************************************************/
typedef union
{
uint8 byte;

struct
{
uint8 ios0 : 1;
uint8 ios1 : 1;
uint8 ios2 : 1;
uint8 ios3 : 1;
uint8 ios4 : 1;
uint8 ios5 : 1;
uint8 ios6 : 1;
uint8 ios7 : 1;
}bit;

}tTIOS;

#define tios *(tTIOS volatile *)(C_REG_BASE_ADDR + 0x040u)

That is the jist of my original post. After struggling with this error
and not understanding the reason, I changed the macro definition to the
following and the compiler was fine with it:

#define tios (*(tTIOS volatile *)(C_REG_BASE_ADDR + 0x040u))

I am confused as to why. I would appreciate any insight into this and
thanks a lot in advance.
 
K

Keith Thompson

jlara said:
The compiler gives the error "request for member 'bit' in something not
a structure or union" for the following code:

/***************************************************

* TIOS : Timer input capture/output compare select
****************************************************/
typedef union
{
uint8 byte;

struct
{
uint8 ios0 : 1;
uint8 ios1 : 1;
uint8 ios2 : 1;
uint8 ios3 : 1;
uint8 ios4 : 1;
uint8 ios5 : 1;
uint8 ios6 : 1;
uint8 ios7 : 1;
}bit;

}tTIOS;

#define tios *(tTIOS volatile *)(C_REG_BASE_ADDR + 0x040u)

That is the jist of my original post. After struggling with this error
and not understanding the reason, I changed the macro definition to the
following and the compiler was fine with it:

#define tios (*(tTIOS volatile *)(C_REG_BASE_ADDR + 0x040u))

I am confused as to why. I would appreciate any insight into this and
thanks a lot in advance.

Macro expansion doesn't work with expressions, it works with sequences
of tokens. The token sequence is interpreted in the context of the
expansion, not in the context of the definition.

It's not at all surprising that a macro definition that's not enclosed
in parentheses would cause problems. Take a look at my previous reply
for an example (the SIX*NINE program). Compile and run the program,
see what output it produces, and understand why it behaves the way it
does. Think about what the the printf statement will look like after
the macros are expanded.

The compiler is not complaining about the macro definition. The only
requirement there is that the expansion needs to be a valid token
sequence. You don't even have to have matched parentheses; the following
would be a perfectly legal macro definition:

#define FOO (*(tTIOS volatile *)(C_REG_BASE_ADDR + 0x040u

The compiler is complaining about an invocation of the macro. Since
you didn't show us either the invocation or the definition of
C_REG_BASE_ADDR, we have no way to guess specifically what the problem
is. But since you have the actual code in front of you, I encourage
you to try to figure this out for yourself. I think you have enough
information to do so. If you get stuck, feel free to post again.

A couple of minor points:

As I mentioned before, macro names are conventionally all-caps; TIOS
would be a better name for your macro that tios. Lower-case macro
names are perfectly legal, of course; this is just a style issue.

The only portable types for bit fields are int, signed int, and
unsigned int (and _Bool in C99); plain int can be treated as either
signed or unsigned. Declaring bit fields of type uint8 is
non-portable, both for the reasons above and because there's no
standard type called uint8. If your compiler or your program provides
a uint8 type (probably a typedef for unsigned char), and your compiler
supports uint8 bit fields as an extension, *and* if declaring the bit
fields as uint8 actually gives you something that declaring them as
unsigned int wouldn't, that's fine.
 
C

CBFalconer

jlara said:
My sincere apologies. I was under the impression that the original
article and all of the threads were visible. I will recap all of
the information once more:
.... snip ...

You don't need to do that. Just post properly, with quotes, in the
first place. The instructions enabling such in google are in my
sig, below. After you learn to do that we will teach you about
proper snipping etc. :)
 
J

jlara

Thanks, I appreciate the help very much. It is crystal clear now. I
will change the unsigned char to unsigned int for portability.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,769
Messages
2,569,582
Members
45,057
Latest member
KetoBeezACVGummies

Latest Threads

Top