2021-04-15 03: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?
2021-04-15 03:49 AM
Which STM32 part? Alignment issue?
Sure it's not trying to Hard Fault?
.CPP file, or compiling as C++?
2021-04-15 03: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.
2021-04-15 04: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;
2021-04-15 04: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
2021-04-15 05: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]
2021-04-15 05:33 AM
This is how the debugger shows the variables
2021-04-15 05:34 AM
2021-04-15 07: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.
2021-04-15 09: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...