Forums

gnu arm - lpc register definitions

Started by klemen_dovrtel November 5, 2006
I have a question about register definitions (in WinARM). I read in
user manual that WD for instance has four registers:
0xE000 0000
0xE000 0004
0xE000 0008
0xE000 000C
Shouldn't be there another REG_8 _pad0[3]; after the REG_8 feed;,
because now WDTV is pointing at wrong adress (it is pointing at
0x0xE000 0009 right?)?

// lpcWD.h
// Watchdog Registers
typedef struct
{
REG_8 mod; // Watchdog Mode Register
REG_8 _pad0[3];
REG32 tc; // Watchdog Time Constant Register
REG_8 feed; // Watchdog Feed Register
REG32 tv; // Watchdog Time Value Register
} wdRegs_t;

//lpc21xx.h
#define WD ((wdRegs_t *)0xE0000000)
// Watchdog Registers
#define WDMOD WD->mod /* Watchdog Mode Register */
#define WDTC WD->tc /* Watchdog Time Constant
Register */
#define WDFEED WD->feed /* Watchdog Feed Register */
#define WDTV WD->tv /* Watchdog Time Value Register */

An Engineer's Guide to the LPC2100 Series

--- In l..., "klemen_dovrtel"
wrote:
>
> I have a question about register definitions (in WinARM). I read in
> user manual that WD for instance has four registers:
> 0xE000 0000
> 0xE000 0004
> 0xE000 0008
> 0xE000 000C
> Shouldn't be there another REG_8 _pad0[3]; after the REG_8 feed;,
> because now WDTV is pointing at wrong adress (it is pointing at
> 0x0xE000 0009 right?)?
>
> // lpcWD.h
> // Watchdog Registers
> typedef struct
> {
> REG_8 mod; // Watchdog Mode Register
> REG_8 _pad0[3];
> REG32 tc; // Watchdog Time Constant
Register
> REG_8 feed; // Watchdog Feed Register
> REG32 tv; // Watchdog Time Value Register
> } wdRegs_t;
>
> //lpc21xx.h
> #define WD ((wdRegs_t *)0xE0000000)
> // Watchdog Registers
> #define WDMOD WD->mod /* Watchdog Mode Register */
> #define WDTC WD->tc /* Watchdog Time Constant
> Register */
> #define WDFEED WD->feed /* Watchdog Feed Register */
> #define WDTV WD->tv /* Watchdog Time Value
Register */
>

I don't think I would do it that way. Among other things, you don't
have the registers defined as 'volatile'.

The LPC214x.h file from Philips does it this way:

/* Watchdog */
#define WDG_BASE_ADDR 0xE0000000
#define WDMOD (*(volatile unsigned long *)(WDG_BASE_ADDR + 0x00))
#define WDTC (*(volatile unsigned long *)(WDG_BASE_ADDR + 0x04))
#define WDFEED (*(volatile unsigned long *)(WDG_BASE_ADDR + 0x08))
#define WDTV (*(volatile unsigned long *)(WDG_BASE_ADDR + 0x0C))

Typedefs and structs are nice but I don't think this is the best place
to use them.

Richard
--- In l..., "rtstofer" wrote:
>
> --- In l..., "klemen_dovrtel"
> wrote:
>>
>> I have a question about register definitions (in WinARM). I read in
>> user manual that WD for instance has four registers:
>> 0xE000 0000
>> 0xE000 0004
>> 0xE000 0008
>> 0xE000 000C
>> Shouldn't be there another REG_8 _pad0[3]; after the REG_8 feed;,
>> because now WDTV is pointing at wrong adress (it is pointing at
>> 0x0xE000 0009 right?)?
>>
>> // lpcWD.h
>> // Watchdog Registers
>> typedef struct
>> {
>> REG_8 mod; // Watchdog Mode Register
>> REG_8 _pad0[3];
>> REG32 tc; // Watchdog Time Constant Register
>> REG_8 feed; // Watchdog Feed Register
>> REG32 tv; // Watchdog Time Value Register
>> } wdRegs_t;
>>
>> //lpc21xx.h
>> #define WD ((wdRegs_t *)0xE0000000)
>> // Watchdog Registers
>> #define WDMOD WD->mod /* Watchdog Mode Register */
>> #define WDTC WD->tc /* Watchdog Time Constant Register */
>> #define WDFEED WD->feed /* Watchdog Feed Register */
>> #define WDTV WD->tv /* Watchdog Time Value Register */
>> I don't think I would do it that way. Among other things, you don't
> have the registers defined as 'volatile'.
>
> The LPC214x.h file from Philips does it this way:
>
> /* Watchdog */
> #define WDG_BASE_ADDR 0xE0000000
> #define WDMOD (*(volatile unsigned long *)(WDG_BASE_ADDR + 0x00))
> #define WDTC (*(volatile unsigned long *)(WDG_BASE_ADDR + 0x04))
> #define WDFEED (*(volatile unsigned long *)(WDG_BASE_ADDR + 0x08))
> #define WDTV (*(volatile unsigned long *)(WDG_BASE_ADDR + 0x0C))
>
> Typedefs and structs are nice but I don't think this is the best
> place to use them.
To have the struct version work correctly, you should make the
wdRegs_t volatile:
#define WD ((volatile wdRegs_t *)0xE0000000)

If you use GCC, structs have the advantage that they allow better
code generation. When you access multiple registers within one
peripheral, such as WDMOD and WDFEED, GCC can load one base address
in a register, and then use different offsets from that base
address. It cannot do that with #defines that just define the full
register addresses.

With the struct definitions, GCC 4.0.1 turns this program:

int main (void)
{
WDMOD = 0x03;
WDFEED = 0xAA;
}

into this code:

main:
mov r3, #-536870912
mov r2, #3
mov r1, #170
str r2, [r3, #0]
str r1, [r3, #8]
bx lr

while with the "direct" definitions, it becomes

main:
mov r2, #3
mov r3, #-536870912
str r2, [r3, #0]
mov r1, #170
add r3, r3, #8
str r1, [r3, #0]
bx lr

The code difference can be larger if the base address isn't a "nice"
number like 0xE0000000.

Note that the ARM compiler (armcc) can do this optimization by
itself, but GCC cannot (yet).

Karl Olsen
--- In l..., "Karl Olsen" wrote:
> If you use GCC, structs have the advantage that they allow better
> code generation. When you access multiple registers within one
> peripheral, such as WDMOD and WDFEED, GCC can load one base
address
> in a register, and then use different offsets from that base
> address. It cannot do that with #defines that just define the
full
> register addresses.
>

Karl,

Mapping structure definitions to hardware is I think just too
problematic to encourage.

You're correct about gcc. I'd make the following points:

- in most cases the efficiency of access makes no difference
whatsoever to system throughput

- if it really does make a difference, there are other ways to
achieve the same efficiency of access that don't rely in knowledge
of how the compiler stores structs to work (use a #define for the
base address and #defines for all the offsets)

Brendan
Brendan Murphy wrote:
>
> --- In lpc2000@yahoogroups .com ,
> "Karl Olsen" wrote:
> > If you use GCC, structs have the advantage that they allow better
> > code generation. When you access multiple registers within one
> > peripheral, such as WDMOD and WDFEED, GCC can load one base
> address
> > in a register, and then use different offsets from that base
> > address. It cannot do that with #defines that just define the
> full
> > register addresses.
> > Karl,
>
> Mapping structure definitions to hardware is I think just too
> problematic to encourage.
>
> You're correct about gcc. I'd make the following points:
>
> - in most cases the efficiency of access makes no difference
> whatsoever to system throughput
>
> - if it really does make a difference, there are other ways to
> achieve the same efficiency of access that don't rely in knowledge
> of how the compiler stores structs to work (use a #define for the
> base address and #defines for all the offsets)
>
I disagree, use the typedef approach. Using #defines can get really
messy. With a typedef you encapsulate the names of registers inside of
a struct, this enforces scope upon the names. With a #define, you don't
take advantage of the compiler and C language to avoid coding errors
(like using a name in the wrong context).

Regards,

TomW
--
Tom Walsh - WN3L - Embedded Systems Consultant
http://openhardware.net http://cyberiansoftware.com http://openzipit.org
"Windows? No thanks, I have work to do..."
----------------
--- In l..., Tom Walsh wrote:
>
> Brendan Murphy wrote:
> >
> > --- In lpc2000@yahoogroups .com
40yahoogroups.com>,
> > "Karl Olsen" wrote:
> > > If you use GCC, structs have the advantage that they allow
better
> > > code generation. When you access multiple registers within one
> > > peripheral, such as WDMOD and WDFEED, GCC can load one base
> > address
> > > in a register, and then use different offsets from that base
> > > address. It cannot do that with #defines that just define the
> > full
> > > register addresses.
> > >
> >
> > Karl,
> >
> > Mapping structure definitions to hardware is I think just too
> > problematic to encourage.
> >
> > You're correct about gcc. I'd make the following points:
> >
> > - in most cases the efficiency of access makes no difference
> > whatsoever to system throughput
> >
> > - if it really does make a difference, there are other ways to
> > achieve the same efficiency of access that don't rely in
knowledge
> > of how the compiler stores structs to work (use a #define for the
> > base address and #defines for all the offsets)
> >
> I disagree, use the typedef approach. Using #defines can get
really
> messy. With a typedef you encapsulate the names of registers
inside of
> a struct, this enforces scope upon the names. With a #define, you
don't
> take advantage of the compiler and C language to avoid coding
errors
> (like using a name in the wrong context).
>

Tom,

In general, I'd strongly agree with you, for the reasons you give:
the pre-processor is in general something to be avoided.

I guess it's a matter of personal opinion which you consider worse:
using the pre-processor or something inherently non-portable like
structs to access hardware registers. BTW when I say "non-portable"
I mean non-poratble between different compilers or even versions of
the same compiler, rather than portable to a different platform.

Brendan.
Brendan Murphy wrote:
>
> --- In lpc2000@yahoogroups .com ,
> Tom Walsh wrote:
> >
> > Brendan Murphy wrote:
> > >
> > > --- In lpc2000@yahoogroups .com > 40yahoogroups. com>,
> > > "Karl Olsen" wrote:
> > > > If you use GCC, structs have the advantage that they allow
> better
> > > > code generation. When you access multiple registers within one
> > > > peripheral, such as WDMOD and WDFEED, GCC can load one base
> > > address
> > > > in a register, and then use different offsets from that base
> > > > address. It cannot do that with #defines that just define the
> > > full
> > > > register addresses.
> > > >
> > >
> > > Karl,
> > >
> > > Mapping structure definitions to hardware is I think just too
> > > problematic to encourage.
> > >
> > > You're correct about gcc. I'd make the following points:
> > >
> > > - in most cases the efficiency of access makes no difference
> > > whatsoever to system throughput
> > >
> > > - if it really does make a difference, there are other ways to
> > > achieve the same efficiency of access that don't rely in
> knowledge
> > > of how the compiler stores structs to work (use a #define for the
> > > base address and #defines for all the offsets)
> > >
> > I disagree, use the typedef approach. Using #defines can get
> really
> > messy. With a typedef you encapsulate the names of registers
> inside of
> > a struct, this enforces scope upon the names. With a #define, you
> don't
> > take advantage of the compiler and C language to avoid coding
> errors
> > (like using a name in the wrong context).
> > Tom,
>
> In general, I'd strongly agree with you, for the reasons you give:
> the pre-processor is in general something to be avoided.
>
> I guess it's a matter of personal opinion which you consider worse:
> using the pre-processor or something inherently non-portable like
> structs to access hardware registers. BTW when I say "non-portable"
> I mean non-poratble between different compilers or even versions of
> the same compiler, rather than portable to a different platform.
>
Yes, it is a matter of personal choice as to how you "get the job
done". Having said that, a #define approach takes the abstract idea of
setting / clearing a bit out of context and exacts a specific set of
steps to affect the operation.

When using a typedef, you are maintaining the abstraction. Keeping the
IDEA of the operation in the abstract on the coding level leaves the
IMPLEMENTATION up to the compiler. Since the compiler is a 'automatic'
process, the compiler (et.al) does the grunt work of implementing your idea.

With a #define you can build complex structures but with a lot of
tedious detail. With a typedef, you keep it in the abstract. For
example, this is the Transmit Status Vector information returned by the
ENC28J60 ethernet chip:
typedef union {
BYTE v[7];
struct {
WORD ByteCount;
unsigned CollisionCount:4;
unsigned CRCError:1;
unsigned LengthCheckError:1;
unsigned LengthOutOfRange:1;
unsigned Done:1;
unsigned Multicast:1;
unsigned Broadcast:1;
unsigned PacketDefer:1;
unsigned ExcessiveDefer:1;
unsigned MaximumCollisions:1;
unsigned LateCollision:1;
unsigned Giant:1;
unsigned Underrun:1;
WORD BytesTransmittedOnWire;
unsigned ControlFrame:1;
unsigned PAUSEControlFrame:1;
unsigned BackpressureApplied:1;
unsigned VLANTaggedFrame:1;
unsigned Zeros:4;
} bits;
} TXSTATUS;

If you wanted to get the number of BytesTransmittedOnWire, how would you
do that using a #define? Something like this?

#define BytesTransmitted 1
number = (WORD) *TxStatusVector [BytesTransmitted];

The problem with a define is that you MUST remember to cast the pointer
value into a WORD, if you forget to do that then you will get an 8bit
value instead. With a typedef, you merely access the desired member of
the structure and 'fahget about it'. ;-) Here it is, still in the
abstraction:

number = *TxStatusVector.BytesTransmitted;

It is really difficult to create a problem for yourself with a
typedef! If, for example, "number" is an "unsigned char" then the
compiler will dump errors or warning back to you. If, when using a
#define, you forget to cast the result to a WORD, the compiler may
silently do it.

OH, keep in mind too that when I determined that "BytesTransmitted" had
an index value of "1", I had to count the bytes to make sure I was
referencing the correct one, with a typedef, you don't need to count
anything (beyond when you initially define the structure).
Regards,

TomW

--
Tom Walsh - WN3L - Embedded Systems Consultant
http://openhardware.net http://cyberiansoftware.com http://openzipit.org
"Windows? No thanks, I have work to do..."
----------------
--- In l..., Tom Walsh wrote:
> When using a typedef, you are maintaining the abstraction.
Keeping the
> IDEA of the operation in the abstract on the coding level leaves
the
> IMPLEMENTATION up to the compiler. Since the compiler is
a 'automatic'
> process, the compiler (et.al) does the grunt work of implementing
your idea.
>

Agreed 100%. Software works on abstract objects, so best to keep
them like that.

> With a #define you can build complex structures but with a lot of
> tedious detail. With a typedef, you keep it in the abstract. For
> example, this is the Transmit Status Vector information returned
by the
> ENC28J60 ethernet chip:
> typedef union {
> BYTE v[7];
> struct {
> WORD ByteCount;
> unsigned CollisionCount:4;
> unsigned CRCError:1;
> unsigned LengthCheckError:1;
> unsigned LengthOutOfRange:1;
> unsigned Done:1;
> unsigned Multicast:1;
> unsigned Broadcast:1;
> unsigned PacketDefer:1;
> unsigned ExcessiveDefer:1;
> unsigned MaximumCollisions:1;
> unsigned LateCollision:1;
> unsigned Giant:1;
> unsigned Underrun:1;
> WORD BytesTransmittedOnWire;
> unsigned ControlFrame:1;
> unsigned PAUSEControlFrame:1;
> unsigned BackpressureApplied:1;
> unsigned VLANTaggedFrame:1;
> unsigned Zeros:4;
> } bits;
> } TXSTATUS;
>

The problem with this is that it relies on information on how the
compiler stores structs, unions, words (little or big-end?), none of
which are defined by the language. You have to:

(a) carefully lay out the structure, using either published info or
(worse) experimentation to ensure you got it right

(b) do this every time you move to a new compiler

(c) do thie every time you upgrade your compiler

That to me is a lot of (error-prone) work.

> If you wanted to get the number of BytesTransmittedOnWire, how
would you
> do that using a #define? Something like this?
>
> #define BytesTransmitted 1
> number = (WORD) *TxStatusVector [BytesTransmitted];
>

Absolutely not! This is non-portable as well.

I'd do something like:

/* pointer to block of memory with TX status vector: */

unsigned char *tx_status_vector;

/* the "abstract" info we want (could be part of struct typedef) */

unsigned int bytes_transmitted;

/* and here's how we go from one to the other: */

bytes_transmitted = tx_status_vector[TX_BYTES_TX_HI] << 8;
bytes_transmitted |= tx_status_vector[TX_BYTES_TX_LO];

> The problem with a define is that you MUST remember to cast the
pointer
> value into a WORD, if you forget to do that then you will get an
8bit
> value instead. With a typedef, you merely access the desired
member of
> the structure and 'fahget about it'. ;-) Here it is, still in
the
> abstraction:
>
> number = *TxStatusVector.BytesTransmitted;
>

This is great, but it is absolutely not portable, which is why I
wouldn't use it in this context (i.e. mapping directly to memory).

> It is really difficult to create a problem for yourself with a
> typedef! If, for example, "number" is an "unsigned char" then the
> compiler will dump errors or warning back to you. If, when using
a
> #define, you forget to cast the result to a WORD, the compiler may
> silently do it.
>

Again: agreed 100%.

> OH, keep in mind too that when I determined
that "BytesTransmitted" had
> an index value of "1", I had to count the bytes to make sure I was
> referencing the correct one, with a typedef, you don't need to
count
> anything (beyond when you initially define the structure).
>

Either way, you only have to define the layout once. With using
#define to define the offsets you just have to match what's in the
data sheet or protocol spec. With defining structs to map onto
memory you have (as well as this) to have an intimate knowledge of
how the compiler stores information, and are left wiuth something
that is likely to cause problems down the line. I know which I'd
prefer.

Brendan
But i still don't know if this register definition are correct.
Shouldn't be there another REG_8 _pad0[3]; after the REG_8 feed;?
Isn't the WDTV pointing at wrong adress (0x0xE000 0009)?

// lpcWD.h
// Watchdog Registers
typedef struct
{
REG_8 mod; // Watchdog Mode Register
REG_8 _pad0[3];
REG32 tc; // Watchdog Time Constant Register
REG_8 feed; // Watchdog Feed Register
REG32 tv; // Watchdog Time Value Register
} wdRegs_t;

//lpc21xx.h
#define WD ((wdRegs_t *)0xE0000000)
// Watchdog Registers
#define WDMOD WD->mod /* Watchdog Mode Register */
#define WDTC WD->tc /* Watchdog Time Constant
Register */
#define WDFEED WD->feed /* Watchdog Feed Register */
#define WDTV WD->tv /* Watchdog Time Value Register */
--- In l..., "klemen_dovrtel"
wrote:
>
> But i still don't know if this register definition are correct.
> Shouldn't be there another REG_8 _pad0[3]; after the REG_8 feed;?
> Isn't the WDTV pointing at wrong adress (0x0xE000 0009)?
>
> // lpcWD.h
> // Watchdog Registers
> typedef struct
> {
> REG_8 mod; // Watchdog Mode Register
> REG_8 _pad0[3];
> REG32 tc; // Watchdog Time Constant Register
> REG_8 feed; // Watchdog Feed Register
> REG32 tv; // Watchdog Time Value Register
> } wdRegs_t;
>
> //lpc21xx.h
> #define WD ((wdRegs_t *)0xE0000000)
> // Watchdog Registers
> #define WDMOD WD->mod /* Watchdog Mode Register */
> #define WDTC WD->tc /* Watchdog Time Constant
> Register */
> #define WDFEED WD->feed /* Watchdog Feed Register */
> #define WDTV WD->tv /* Watchdog Time Value Register */

Unless you tell the compiler to "pack" the struct fields, it most
likely inserts padding between feed and tv so that the 32-bit tv
value ends up on an aligned address, and it then happens to work.
Best to check for this and other potential problems by checking if
the final size of the structure is as expected.

In my own struct definitions I just declare all registers (except the
explicitly halfword- and byte-addressable FIO registers) as 32-bit.

Karl Olsen