Have an issue with reading from union of bitfield structures. The compilation of the program is successful, however the execution interrupts and ends up in the default interrupt handler, which by default is an endless loop. Could this be a compiler issue?
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Email to a Friend
- Report Inappropriate Content
‎2021-04-15 3:39 AM
To pars binary information I created a series of bitfield structures
typedef struct PD_SOURCE_PDO_CAPABILITY {
uint32_t MAX_CURRENT_UNITS : 10;
uint32_t VOLTAGE_UNITS : 10;
uint32_t PEAK_CURRENT : 2;
uint32_t RESERVED_BITS22_23 : 2;
uint32_t EXTENDED_MSG_SUPPORT : 1;
uint32_t DUAL_ROLE_DATA_ABLE : 1;
uint32_t USB_CAPABLE : 1;
uint32_t UNCONSTRAINED_POWER : 1;
uint32_t USB_SUSPEND_SUPPORT : 1;
uint32_t DUAL_ROLE_POWER_ABLE : 1;
uint32_t PDO_TYPE : 2;
} PD_SOURCE_PDO_CAPABILITY_T;
typedef struct PD_SOURCE_PDO_FIXED {
uint32_t MAX_CURRENT_UNITS : 10;
uint32_t VOLTAGE_UNITS : 10;
uint32_t PEAK_CURRENT : 2;
uint32_t RESERVED_BITS22_29 : 8;
uint32_t PDO_TYPE : 2;
} PD_SOURCE_PDO_FIXED_T;
typedef struct PD_SOURCE_PDO_BATTERY
{
uint32_t MAX_POWER_UNITS : 10;
uint32_t MIN_VOLTAGE_UNITS : 10;
uint32_t MAX_VOLTAGE_UNITS : 10;
uint32_t PDO_TYPE : 2;
} PD_SOURCE_PDO_BATTERY_T;
typedef struct PD_SOURCE_PDO_VARIABLE
{
uint32_t MAX_CURRENT_UNITS : 10;
uint32_t MIN_VOLTAGE_UNITS : 10;
uint32_t MAX_VOLTAGE_UNITS : 10;
uint32_t PDO_TYPE : 2;
} PD_SOURCE_PDO_VARIABLE_T;
typedef struct PD_SOURCE_PDO_AUGMENTED
{
uint32_t MAX_CURRENT_UNITS : 7;
uint32_t RESERVED_BIT7 : 1;
uint32_t MIN_VOLTAGE_UNITS : 8;
uint32_t RESERVED_BIT16 : 1;
uint32_t MAX_VOLTAGE_UNITS : 8;
uint32_t RESERVED_BITS25_26 : 2;
uint32_t POWER_LIMITED : 1;
uint32_t RESERVED_BITS28_29 : 2;
uint32_t PDO_TYPE : 2;
} PD_SOURCE_PDO_AUGMENTED_T;
Because a stream of data can contain either of above bitfields I created a union of bitfield structs.
typedef union PD_SOURCE_PDO
{
uint32_t SCALAR;
PD_SOURCE_PDO_CAPABILITY_T CAPABILITY_PDO;
PD_SOURCE_PDO_FIXED_T FIXED_PDO;
PD_SOURCE_PDO_BATTERY_T BATTERY_PDO;
PD_SOURCE_PDO_VARIABLE_T VARIABLE_PDO;
PD_SOURCE_PDO_AUGMENTED_T AUGMENTED_PDO;
} PD_SOURCE_PDO_T;
the code that is actually handling the data streams is as follows
for (PD_ACTIVE_OBJECT_T Object = 0; Object < NumOfObjects; Object++) {
PD_SOURCE_PDO_T* SOURCE = (PD_SOURCE_PDO_T*)(pData + (Object * sizeof(PD_SOURCE_PDO_T)));
PD_POWER_OBJECT_T* POWER = (PD_POWER_OBJECT_T*)&pInstance->PowerObject[Object];
if (Object == 0 ) {
pInstance->DataEnabled = SOURCE->CAPABILITY_PDO.USB_CAPABLE;
pInstance->Unconstrained = SOURCE->CAPABILITY_PDO.UNCONSTRAINED_POWER;
pInstance->ExtMsgSupport = SOURCE->CAPABILITY_PDO.EXTENDED_MSG_SUPPORT;
pInstance->DualRoleData = SOURCE->CAPABILITY_PDO.DUAL_ROLE_DATA_ABLE;
pInstance->DualRolePower = SOURCE->CAPABILITY_PDO.DUAL_ROLE_POWER_ABLE;
pInstance->SuspendEnabled = SOURCE->CAPABILITY_PDO.USB_SUSPEND_SUPPORT;
}
POWER->PowerSource = SOURCE->CAPABILITY_PDO.PDO_TYPE;
//POWER->PowerSource = READ_MASKED(SOURCE->SCALAR, 2, 30);
if (POWER->PowerSource == PD_POWER_SOURCE_BATTERY) {
//POWER->MaxVoltage = SOURCE->BATTERY_PDO.MAX_VOLTAGE_UNITS * 50;
//POWER->MinVoltage = SOURCE->BATTERY_PDO.MIN_VOLTAGE_UNITS * 50;
//POWER->Power = SOURCE->BATTERY_PDO.MAX_POWER_UNITS * 250;
//POWER->Current = (POWER->Power/POWER->MinVoltage)*1000;
} else if (POWER->PowerSource == PD_POWER_SOURCE_VARIABLE) {
//POWER->MaxVoltage = SOURCE->VARIABLE_PDO.MAX_VOLTAGE_UNITS * 50;
//POWER->MinVoltage = SOURCE->VARIABLE_PDO.MIN_VOLTAGE_UNITS * 50;
//POWER->Current = SOURCE->VARIABLE_PDO.MAX_CURRENT_UNITS * 10;
//POWER->Power = ((POWER->MaxVoltage*POWER->Current)/1000);
} else if (POWER->PowerSource == PD_POWER_SOURCE_AUGMENTED) {
//POWER->MaxVoltage = SOURCE->AUGMENTED_PDO.MAX_VOLTAGE_UNITS * 100;
//POWER->MinVoltage = SOURCE->AUGMENTED_PDO.MIN_VOLTAGE_UNITS * 100;
//POWER->Current = SOURCE->AUGMENTED_PDO.MAX_CURRENT_UNITS * 50;
//POWER->Power = ((POWER->MaxVoltage*POWER->Current)/1000);
} else { //PD_POWER_SOURCE_FIXED
//PD_VOLTAGE_T VoltUnits = READ_MASKED(SOURCE->SCALAR, 10, 10);
PD_VOLTAGE_T VoltUnits = SOURCE->FIXED_PDO.VOLTAGE_UNITS;
//PD_CURRENT_T AmpUnits = READ_MASKED(SOURCE->SCALAR, 10, 0);
PD_CURRENT_T AmpUnits = SOURCE->FIXED_PDO.MAX_CURRENT_UNITS;
POWER->MaxVoltage = VoltUnits * 50;
POWER->MinVoltage = POWER->MaxVoltage;
POWER->Current = AmpUnits * 10;
POWER->Power = ((POWER->MaxVoltage*POWER->Current)/1000);
}
}
The issue is triggered when reading from a pointer variable SOURCE with is a union of bitfield type.
This seams to be working
POWER->PowerSource = SOURCE->CAPABILITY_PDO.PDO_TYPE;
08002a78: ldr r3, [r7, #28]
08002a7a: ldrb r3, [r3, #3]
08002a7c: lsls r3, r3, #24
08002a7e: lsrs r3, r3, #30
08002a80: uxtb r3, r3
08002a82: sxtb r2, r3
08002a84: ldr r3, [r7, #24]
08002a86: strb r2, [r3, #16]
But this fails to execute
PD_VOLTAGE_T VoltUnits = SOURCE->FIXED_PDO.VOLTAGE_UNITS;
08002aa6: ldr r3, [r7, #28]
08002aa8: ldr r3, [r3, #0]
08002aaa: lsls r3, r3, #12
08002aac: lsrs r3, r3, #22
08002aae: uxth r3, r3
08002ab0: str r3, [r7, #20]
Causing a jump to
.section .text.Default_Handler,"ax",%progbits
Default_Handler:
Infinite_Loop:
b Infinite_Loop
.size Default_Handler, .-Default_Handler
At this point I'm clue less of what's going on?
Even stranger if I compile the code as below the previous assignment statement is now also failing?
POWER->PowerSource = READ_MASKED(SOURCE->SCALAR, 2, 30);
08002a78: ldr r3, [r7, #28]
08002a7a: ldr r3, [r3, #0]
08002a7c: lsrs r3, r3, #30
08002a7e: sxtb r2, r3
08002a80: ldr r3, [r7, #24]
08002a82: strb r2, [r3, #16]
What's up doc?
- Labels:
-
Power
-
STM32G0 Series
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Email to a Friend
- Report Inappropriate Content
‎2021-04-15 3:49 AM
Which STM32 part? Alignment issue?
Sure it's not trying to Hard Fault?
.CPP file, or compiling as C++?​
Up vote any posts that you find helpful, it shows what's working..
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Email to a Friend
- Report Inappropriate Content
‎2021-04-15 3:53 AM
Part is STM32G071
This is C only and I believe compiling as C only
Hard Fault caused by? When commenting this phrase its working fine.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Email to a Friend
- Report Inappropriate Content
‎2021-04-15 4:41 AM
Concerning the Alignment.
Would you consider this bit order to be correct?
typedef struct PD_SOURCE_PDO_CAPABILITY
{
uint32_t MAX_CURRENT_UNITS : 10; /*!< BITS 0-9 Maximum current in 10mA units */
uint32_t VOLTAGE_UNITS : 10; /*!< BITS 10-19 Voltage in 50mV units */
uint32_t PEAK_CURRENT : 2; /*!< BITS 20-21 Peak current bits */
uint32_t RESERVED_BITS22_23 : 2; /*!< BITS 22-23 Shall be set to zero */
uint32_t EXTENDED_MSG_SUPPORT : 1; /*!< BIT 24 Shall be set if source supports unchuncked extended messages */
uint32_t DUAL_ROLE_DATA_ABLE : 1; /*!< BIT 25 Shall be set if source supports dual role data */
uint32_t USB_CAPABLE : 1; /*!< BIT 26 Shall be set if source is USB communication capable */
uint32_t UNCONSTRAINED_POWER : 1; /*!< BIT 27 Shall be set if source has adequate power supply for charging external devices*/
uint32_t USB_SUSPEND_SUPPORT : 1; /*!< BIT 28 Shall be set if source supports USB suspend */
uint32_t DUAL_ROLE_POWER_ABLE : 1; /*!< BIT 29 Shall be set if source supports dual role power */
uint32_t PDO_TYPE : 2; /*!< BITS 30-31 Source capability object is always defined as b00 = fixed */
} PD_SOURCE_PDO_CAPABILITY_T;
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Email to a Friend
- Report Inappropriate Content
‎2021-04-15 4:55 AM
In Cortex-M0/M0+, you must meticulously obey alignment. Typecasting pointers is one good way to arrive at unaligned positions.
Here, it appears to me, that pData is word-unaligned already.
Learn how to debug hardfaults, it's a useful skill.
JW
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Email to a Friend
- Report Inappropriate Content
‎2021-04-15 5:22 AM
Thanks for sharing this comment, but need some additional info
Can confirm pData is a pointer to uint8_t type
How would you reassign SOURCE
PD_SOURCE_PDO_T* SOURCE = (PD_SOURCE_PDO_T*)(pData + (Object * sizeof(PD_SOURCE_PDO_T)));
Following doesn't seam to change things.
PD_SOURCE_PDO_T* SOURCE = (PD_SOURCE_PDO_T*)((uint32_t*)pData + Object);
POWER->PowerSource = READ_MASKED(SOURCE->SCALAR, 2, 30);
08002a78: ldr r3, [r7, #28]
08002a7a: ldr r3, [r3, #0]
08002a7c: lsrs r3, r3, #30
08002a7e: sxtb r2, r3
08002a80: ldr r3, [r7, #24]
08002a82: strb r2, [r3, #16]
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Email to a Friend
- Report Inappropriate Content
‎2021-04-15 5:33 AM
This is how the debugger shows the variables
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Email to a Friend
- Report Inappropriate Content
‎2021-04-15 5:34 AM
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Email to a Friend
- Report Inappropriate Content
‎2021-04-15 7:33 AM
In Cortex-M0/M0+, you must meticulously obey alignment.
This, the CM0(+) does not permit spanning 32-bit read/write access, other CM families will tolerate misaligned LDR/STR, but not LDRD/STRD
In your code example both [r7] and [r3] memory accesses must occur on 32-bit (4 byte) address boundaries.
If sizeof(PD_SOURCE_PDO_T) is ODD, the first set of data might work, the second set will not.
0x2000099A fails the alignment test
Suggest also you implement a proper Hard Fault Handler that can output diagnostic info. I've published these multiple times to the forum.
Up vote any posts that you find helpful, it shows what's working..
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Email to a Friend
- Report Inappropriate Content
‎2021-04-15 9:16 AM
This seams to work
uint32_t Offset = Object * sizeof(PD_SOURCE_PDO_T);
PD_SOURCE_PDO_T SOURCE; memcpy(&SOURCE, (pData + Offset), sizeof(PD_SOURCE_PDO_T));
but boy would I like to get a different and more intelligent approach then to copy data that's already in a buffer...
