the following structure is a structure of registers with type timer_t
typedef struct { /*!< TIMER0 Structure */
uint32_t CFG; /*!< GPTM Configuration */
uint32_t TAMR; /*!< GPTM Timer A Mode */
uint32_t TBMR; /*!< GPTM Timer B Mode */
uint32_t CTL; /*!< GPTM Control */
uint32_t SYNC; /*!< GPTM Synchronize */
uint32_t RESERVED;
uint32_t IMR; /*!< GPTM Interrupt Mask */
uint32_t RIS; /*!< GPTM Raw Interrupt Status */
uint32_t MIS; /*!< GPTM Masked Interrupt Status */
uint32_t ICR; /*!< GPTM Interrupt Clear */
uint32_t TAILR; /*!< GPTM Timer A Interval Load */
uint32_t TBILR; /*!< GPTM Timer B Interval Load */
uint32_t TBMATCHR; /*!< GPTM Timer B Match */
uint32_t TAPR; /*!< GPTM Timer A Prescale */
uint32_t TBPR; /*!< GPTM Timer B Prescale */
uint32_t TAPMR; /*!< GPTM TimerA Prescale Match */
uint32_t TBPMR; /*!< GPTM TimerB Prescale Match */
uint32_t TAR; /*!< GPTM Timer A */
uint32_t TBR; /*!< GPTM Timer B */
uint32_t TAV; /*!< GPTM Timer A Value */
uint32_t TBV; /*!< GPTM Timer B Value */
uint32_t RTCPD; /*!< GPTM RTC Predivide */
uint32_t TAPS; /*!< GPTM Timer A Prescale Snapshot */
uint32_t TBPS; /*!< GPTM Timer B Prescale Snapshot */
uint32_t TAPV; /*!< GPTM Timer A Prescale Value */
uint32_t TBPV; /*!< GPTM Timer B Prescale Value */
uint32_t RESERVED1[981];
uint32_t PP; /*!< GPTM Peripheral Properties */
}timer_t;
and this #define TIMER0 ((timer_t *) TIMER0_BASE)
is used to dereference them so I can write on them as TIMER0_BASE
is the base address of the used module. now I want to declare another bit field with type timerAModeBitField_t;
for every register something to be like this
typedef struct timerAModeBitField{
uint32_t timerAMode : 2;
uint32_t timerACaptMode : 1;
uint32_t timeraPWMMode : 1;
uint32_t timerACountDir : 1;
uint32_t timerAMatchIntEn : 1;
uint32_t timerAWaitOnTrig : 1;
uint32_t timerASnapShotMode : 1;
uint32_t timerAIntervalLoad : 1;
}timerAModeBitField_t;
so I can directly write to their specific bits is it possible to implement something like that so I can first dereference the register and then dereference the bit field TIMER0->TAMR->timerAmode = 0x04
CodePudding user response:
In portable C, the layout of bit-fields is unspecified. So you can't use bit-fields to describe a specific layout in portable C.
However, if you're writing code for a specific architecture, your C implementation may have additional guarantees. On Arm platforms, in particular, the Arm Architecture Procedure Call Standard (AAPCS) applies. It has a rule for bit-fields, and specifically for bit-fields in C and C . If the bit-field description were provided by your vendor and the peripheral was intended to be used on an Arm processor, the AAPCS guarantees that you're both making the same assumptions on the data layout, so your code will use the intended data layout.
However using the same data layout is not sufficient! You also have to ensure that the compiler will do the right thing when a field is written in one way (say, through the uint32_t
) and then read back in the other way (say, through the bit-field). C compilers are generally allowed to assume that when memory is accessed through different types, it's different memory. This allows compilers to optimize code better.
There are several exceptions which I won't go into here, but in general you can't assume that writing memory in one way and reading it back in some other way will give consistent results. Accessing the same memory in different ways is called aliasing. What is the strict aliasing rule? has a very good explanation of how this applies to C. Be careful: violating the aliasing rules can often result in code that fails only at certain levels of optimization, and only in certain contexts (e.g. depending on the number of registers that the compiler had available for a particular block of code), so this can be hard to debug. In particular, beware that casts can often get around compiler warnings, but not against aliasing bugs: casts just tell the compiler to stop complaining about broken code, they don't make the code less broken.
One way to let the compiler know that you're accessing the same memory through different types is to put those types in a union. (This is guaranteed in C11, and not officially guaranteed but widely implemented in C99.) That is:
typedef union {
uint32_t value;
timerAModeBitField bits;
} timerAModeUnion;
typedef struct {
…
timerAModeUnion TAMR;
…
} timer_t;
Then you can reliably do things like bulk-write TIMER0->TAMR.value
and read a specific bit-field via TIMER0->TAMR.bits.timerAmode
.
CodePudding user response:
You can use the (inner) structure as the data type of an element of another (outer) structure, like this:
typedef struct timerAModeBitField {
uint32_t timerAMode : 2;
uint32_t timerACaptMode : 1;
uint32_t timeraPWMMode : 1;
uint32_t timerACountDir : 1;
uint32_t timerAMatchIntEn : 1;
uint32_t timerAWaitOnTrig : 1;
uint32_t timerASnapShotMode : 1;
uint32_t timerAIntervalLoad : 1;
uint32_t padding : 23;
} timerAModeBitField_t;
typedef struct { /*!< TIMER0 Structure */
uint32_t CFG; /*!< GPTM Configuration */
timerAModeBitField_t TAMR; /*!< GPTM Timer A Mode */
uint32_t TBMR; /*!< GPTM Timer B Mode */
uint32_t CTL; /*!< GPTM Control */
uint32_t SYNC; /*!< GPTM Synchronize */
uint32_t RESERVED;
uint32_t IMR; /*!< GPTM Interrupt Mask */
uint32_t RIS; /*!< GPTM Raw Interrupt Status */
uint32_t MIS; /*!< GPTM Masked Interrupt Status */
uint32_t ICR; /*!< GPTM Interrupt Clear */
uint32_t TAILR; /*!< GPTM Timer A Interval Load */
uint32_t TBILR; /*!< GPTM Timer B Interval Load */
uint32_t TBMATCHR; /*!< GPTM Timer B Match */
uint32_t TAPR; /*!< GPTM Timer A Prescale */
uint32_t TBPR; /*!< GPTM Timer B Prescale */
uint32_t TAPMR; /*!< GPTM TimerA Prescale Match */
uint32_t TBPMR; /*!< GPTM TimerB Prescale Match */
uint32_t TAR; /*!< GPTM Timer A */
uint32_t TBR; /*!< GPTM Timer B */
uint32_t TAV; /*!< GPTM Timer A Value */
uint32_t TBV; /*!< GPTM Timer B Value */
uint32_t RTCPD; /*!< GPTM RTC Predivide */
uint32_t TAPS; /*!< GPTM Timer A Prescale Snapshot */
uint32_t TBPS; /*!< GPTM Timer B Prescale Snapshot */
uint32_t TAPV; /*!< GPTM Timer A Prescale Value */
uint32_t TBPV; /*!< GPTM Timer B Prescale Value */
uint32_t RESERVED1[981];
uint32_t PP; /*!< GPTM Peripheral Properties */
} timer_t;
Then you can access mode bits in this way:
TIMER0->TAMR.timerAmode = 2;
Note: You should not assign 0x04 to a bitfield of width 2, the width of the value is too big. Possible values range from 0 to 3.
EDIT:
Let's minize the example:
typedef union {
struct {
unsigned int mode: 2;
unsigned int padding: 30;
};
uint32_t value;
} control_t;
typedef struct {
control_t control;
uint32_t stuff;
} module_t;
You can always obtain the address of an element of a structure:
module_t module;
control_t* mode_p = &module.control;
mode_p->mode = 3; // write only to 2 bits
uint32_t v = mode_p->value; // read all 32 bits
Via the union you can use either the complete control register or conveniently access just some bits:
module.control.value = 0x12345678;
module.control.mode = 2;
Don't fall into the trap to think too much from a machine code perspective. Use the abstraction of the higher level language to gain better source control by the compiler. Avoid casts as much as possible, since they tell the compiler to think, "The programmer is always right, who am I to doubt her?" Even in cases where your code has errors.
Caution: In fact it is not defined by the C standard how exactly bitfields are allocated. However, compilers do this in a reproducable way, it might well be documented. Make sure you guard your code with a check on the compiler you validate for your usage.
CodePudding user response:
You could try this approach using unnamed members:
#include <stdint.h>
typedef struct {
...
union {
struct {
uint32_t timerAMode : 2;
uint32_t timerACaptMode : 1;
uint32_t timeraPWMMode : 1;
uint32_t timerACountDir : 1;
uint32_t timerAMatchIntEn : 1;
uint32_t timerAWaitOnTrig : 1;
uint32_t timerASnapShotMode : 1;
uint32_t timerAIntervalLoad : 1;
};
uint32_t TAMR;
};
...
} timer_t;
#define TIMER0_BASE (0x40001000)
volatile timer_t *TIMER0 = (timer_t*) TIMER0_BASE;
int main(void)
{
TIMER0->TAMR = 0x1234;
TIMER0->timerAMode = 2;
}
By using unnamend members you can access members of the union/struct directly without adding a field name for that union.
This allows accessing TAMR
at once as well as the separate fields of it.