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 09:30 AM
Well the compiler assumes that the structure is aligned.
The problem here is that you cast it from pData which is unaligned.
On the old ARM7/ARM9 platforms it was generally advisable to memcpy() to an aligned auto-variable copy when dealing with memory or file packed/byte aligned structures.
The alternative is for the compile to know something is unaligned, or potentially, and then construct 32-bit words by reading as 4 individual byte accesses.
One could also test the pointer alignment, and optionally copy to an aligned buffer when it is not.
2021-04-15 03:13 PM
> different and more intelligent approach
Then you have to invent it yourself - either write in assembler, or write yourself a compiler from whatever higher level description (a.k.a. language) you wish. This is not trolling, I mean it.
The traditional standpoint of programming languages is to provide you with convenience like math-like operations and portability - a certain level of abstraction, in exchange for some inefficiency and certain loss of control. If you wish to regain that control - e.g. by prescribing particular layout of data - you must give up the covenience - i.e. the high-level language does not work anymore. In particular, in C, there are two ways to prescribe data layout - pointer cast, and unions. Both are directly warned against in the standard (according to standard, both result in undefined behaviour). That they are widely used (and I use them, too) does not make them any less wrong.
Packed bitfields with assumed arrangement are in fact the third such way - and you've conveniently omitted the fact that you've packed the bitfields - and it's even more evil than those traditional two. And I do use them, too. Extensively. But then I also try to avoid Cortex-M0/M0+ as far as I can (and on those, I don't use any of the abovementioned techniques). Cortex-M3/M4 luckily has much less alignment constraints (there are some, and are source of painful gotchas too).
The only true portable method is, if you have buffer of bytes, treat them as bytes, and rearrange them manually, in that most painful way.
JW