M0 storage access address alignment-problems encountered in debugging the hardware platform demo
The data transmission format in the demo provided by the hardware platform is as follows. During each data transmission, the valid data is packaged and sent after a fixed packet header is added.
The demo provided on the official website is used to send messages by lighting, so in order to distinguish these messages and some other additional functions, they are in the effective data (that is, the variable-length package body packaged by protoalbuf) it defines a packet header.
Typedef struct
{
Uint8_tm_magicCode [2];
Uint16_tm_version;
Uint16_tm_totalLength;
Uint16_tm_cmdid;
Uint16_tm_seq;
Uint16_tm_errorCode;
} BlueDemoHead;
For example, m_cmdid is used to distinguish between a light-on or a light-on. This is just for the demo of lighting. We don't need this header when using it. It is only necessary to fix the header.
PS: the Auth of blueDemoHead when it is just connected. It does not have this header in the init process.
During the second development using the demo, we kept the internal headers for the lighting demo for scalability. Therefore, the valid data is packaged with blueDemoHead + followed by a fixed header. Finally sent. Also, the received data is of this type.
That is, the problem occurs here. In the demo, the Unpack code for the pushed data directly converts the uint8_t type pointer to the BlueDemoHead type pointer, in this way, you can easily access data in the form of struct.
Decommission code in the mpbledemo2.c file: only check the decommission part of the push package.
// Data indicates all received data.
Int mpbledemo2_data_consume_func (uint8_t * data, uint32_t len ){
................
Switch (ntohs (fix_head-> ntoid ))
{
CaseECI_resp_auth:
....................
Break;
CaseECI_resp_init:
........................
Break;
CaseECI_push_recvData:
RecvDataPush * recvDatPush;
// Unpackage starts from the end of the Fixed Header.
RecvDatPush = epb_unpack_recv_data_push (data + fix_head_len, len-fix_head_len );
.........................
// After unpacking, recvDatPush-> data. data points to valid data (blueDemoHead + LOAD // data)
// There is a problem with the forced conversion here !!!!
BlueDemoHead * bledemohead = (BlueDemoHead *) recvDatPush-> data. data;
........................
// The reason is that the unpackage function actually points recvDatPush-> data. data to the valid data section.
// That is, an offset pointing to the parameter data of mpbledemo2_data_consume_func
// Starting from this offset to recvDatPush-> data. len is the valid data (blueDemoHead + LOAD // data)
// The following code may be faulty, because recvDatPush-> data. data points to the biased
// The physical address to be moved may be non-semi-aligned, so bledemohead is also non-semi-aligned.
// The m_cmdid member's offset in the struct is 6, so the Member's address may also be a non-half-character pair
// Qi, So if you use the BLE chip MCU data access needs address alignment, then the following
// Access becomes a problem, while the 51822 ble chip is a M0 processor that requires a half-character access address.
// The half-character alignment. Therefore, if you execute the following code, a hardware error may occur.
If (ntohs (bledemohead-> m_relative ID) = openLightPush ){
To sum up, the root cause of possible errors is
This demo runs on the MCU that requires data access to address alignment. If bledemohead is assigned a value, if it is not semi-aligned, A hardware error occurs when the m_id ID Member with a 6-byte offset is accessed by a pointer.
The error may occur because recvDatPush-> data. the final address pointed to by data may be semi-aligned or not. If yes, no error will occur. If not, an error will occur when executing the pointer to access members.
During the use process, we had been operating normally before. Later, when a large amount of data was transferred, it contained more than 150 bytes. The problem of non-alignment access was discovered only after a crash.
The above explanation is a bit Abstract: I caught the data processing process for your convenience:
The following is a normal situation: data stores all the data sent, and the storage address is 0x20004388. As mentioned above, the unpackage function is to push recvDatPush-> data. data points to the starting position of valid data, as shown in the following figure: remove fixed Baotou fe 0100 53 7531 0 00 and add protoalbuf to the preceding 0a 0012 44 When packaging the function, the valid data starting from the offset 12 is printed from the address 0x20004388 + 12 = 0x20004394. The address is semi-aligned, so converting it to the structure pointer bledemohead is also semi-aligned. Because the m_id ID offset in bledemohead is 6, the address is also semi-aligned, therefore, bledemohead-> m_mongoid will not cause problems during access.
The following is an exception:
The data storage address is still 0x20004388, but the offset of the valid data is added when the 8-byte Fixed Header + 5-byte protoalbuf is packaged, so the offset is 13 bytes.
0x20004388 + 13 = 0x20004395. The address is non-semi-aligned. Therefore, a non-aligned access is performed when the bledemohead member m_cmdid is accessed. Therefore, a hardware error exception occurs.
According to the above analysis, the cause of the problem is the offset. The test found that when the data volume is small, the additional data added before the valid data is only 4 bytes, And the Fixed Header is 8 bytes, therefore, the offset of valid data is 12 bytes.
The Data address must be semi-aligned, so the address at the 12-byte offset must also be semi-aligned, so no problem will occur.
When there is a large amount of data, we can find that the additional data added by the protoalbuf packaging function except the 8-byte Fixed Header is 5 bytes, this eventually leads to the 13 offset of valid data, resulting in non-semi-word address alignment.
At present, it is not tested whether or not the data added during protoalbuf packaging will increase when there is more data, for example, 6, so it can be "normal" again. However, after all, there are bugs.
There are many solutions. For example, if you do not use the bledemohead in the demo, you can define a simpler format. For example, you can add two bytes before the data to differentiate the meaning of each data representation, in addition, the data is accessed in one byte and one byte during parsing. RecvDatPush-> data. data [0] = aa & recvDatPush-> data. data [1] = 0xBB indicates a type of data, while recvDatPush-> data. data [0] = 0xAA & recvDatPush-> data. data [1] = 0xCC indicates another type of data .. Or the bledemohead header is still used, but the data access after unpacking should not be accessed by converting to a struct pointer and then accessing members, instead, the unwrapped uint8_t pointer can be directly accessed in one byte, such as recvDatPush-> data. whether data [0] is equal to or not, recvDatPush-> data. whether data [1] is equal to or not, recvDatPush-> data. what is data [2 ........ This method is used to differentiate different types of data.