cancel
Showing results for 
Search instead for 
Did you mean: 

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?

Softronic Solutions
Associate III

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?

11 REPLIES 11

Which STM32 part? Alignment issue?

Sure it's not trying to Hard Fault?

.CPP file, or compiling as C++?​

Tips, Buy me a coffee, or three.. PayPal Venmo
Up vote any posts that you find helpful, it shows what's working..
Softronic Solutions
Associate III

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.

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;

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

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]

This is how the debugger shows the variables

0693W000008zeiHQAQ.png

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.

Tips, Buy me a coffee, or three.. PayPal Venmo
Up vote any posts that you find helpful, it shows what's working..
Softronic Solutions
Associate III

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...