I used keil mdk v5.1 for arm to develop my project. I encounterd a serious problem about LDRD instruction. Look at c codes below:
#include <stdint.h> char b[2]; char c[4]; char d[2]; typedef struct { uint32_t m_front; /* ¶ÓÍ· */ uint32_t m_rear; /* ¶Óβ */ uint16_t m_maxData; /* ¶ÓÁÐÖÐÔÊÐí´æ´¢µÄÊý¾Ý¸öÊý */ uint8_t (* ReadEmpty)(); /* ¶Á¿Õ´¦Àíº¯Êý */ uint8_t (* WriteFull)(); /* дÂú´¦Àíº¯Êý */ } DataQueue; void SystemInit() { } void main() { DataQueue* p = (DataQueue*)c; char* q = c; char* xx = b; char* yy = d; if (p->m_front == p->m_rear) { int i = 0; } }
disassembly of part of c codes is below
28: if (p->m_front == p->m_rear) 29: { 0x08000194 E9D04500 LDRD r4,r5,[r0,#0] 0x08000198 42AC CMP r4,r5 0x0800019A D101 BNE 0x080001A0 30: int i = 0; 0x0800019C 2400 MOVS r4,#0x00 31: }
I started a debug session. When I stepped over sentance "if (p->m_front == p->m_rear)", I found that program crashed(Hard fault error).
i thought that LDRD need value of r0 to a multiple of 4, but r0 value was 0x20000002 at this time.
so i have to add __align(4) keyword to make sure that address of "c" is multiple of 4 and everything is fine. you can see the codes below
#include <stdint.h> char b[2]; __align(4) char c[4]; char d[2]; typedef struct { uint32_t m_front; /* ¶ÓÍ· */ uint32_t m_rear; /* ¶Óβ */ uint16_t m_maxData; /* ¶ÓÁÐÖÐÔÊÐí´æ´¢µÄÊý¾Ý¸öÊý */ uint8_t (* ReadEmpty)(); /* ¶Á¿Õ´¦Àíº¯Êý */ uint8_t (* WriteFull)(); /* дÂú´¦Àíº¯Êý */ } DataQueue; void SystemInit() { } void main() { DataQueue* p = (DataQueue*)c; char* q = c; char* xx = b; char* yy = d; if (p->m_front == p->m_rear) { int i = 0; } }
i was confused if this was an bug of compiler? Please help me
Thanks a lot to Broeker and Westermark. I've learned so much from your replies. I was intent on writing a queue and source code is below.
typedef struct { INT8U m_addr; INT8U m_length; INT8U m_buf[8]; }QueueDataType; typedef struct { INT32U m_front; INT32U m_rear; INT16U m_maxData; INT8U (* ReadEmpty)(); INT8U (* WriteFull)(); QUEUE_DATA_TYPE m_buf[1]; } DataQueue; uint8_t QueueCreate(void *Buf, uint32_t SizeOfBuf, uint8_t (* ReadEmpty)(), uint8_t (* WriteFull)() ) { DataQueue *Queue; if (Buf != NULL && SizeOfBuf >= (sizeof(DataQueue) + sizeof(QUEUE_DATA_TYPE))) { Queue = (DataQueue *)Buf; Queue->m_maxData = (SizeOfBuf - (uint32_t)sizeof(DataQueue)) / sizeof(QUEUE_DATA_TYPE); Queue->m_front = 0; Queue->m_rear = 0; Queue->ReadEmpty = ReadEmpty; Queue->WriteFull = WriteFull; return QUEUE_OK; } else { return NOT_OK; } } uint8_t QueueRead(QUEUE_DATA_TYPE *Ret, void *Buf) { uint8_t err; DataQueue *Queue; err = NOT_OK; if (Buf != NULL && Ret != NULL) { Queue = (DataQueue *)Buf; if (Queue->m_rear != Queue->m_front) { uint32_t front = (Queue->m_front + 1) % Queue->m_maxData; *Ret = Queue->m_buf[front]; Queue->m_front = front; err = QUEUE_OK; } else { err = QUEUE_EMPTY; if (Queue->ReadEmpty != NULL) { err = Queue->ReadEmpty(Ret, Queue); } } } return err; } uint8_t QueueWrite(void *Buf, QUEUE_DATA_TYPE* Data) { uint8_t err; DataQueue *Queue; err = NOT_OK; if (Buf != NULL && Data != NULL) { uint32_t rear = 0; Queue = (DataQueue *)Buf; rear = (Queue->m_rear + 1) % Queue->m_maxData; if (rear != Queue->m_front) { Queue->m_buf[rear] = *Data; Queue->m_rear = rear; err = QUEUE_OK; } else { err = QUEUE_FULL; if (Queue->WriteFull != NULL) { err = Queue->WriteFull(Queue, Data, Q_WRITE_MODE); } } } return err; }
I thought that user can define an global buffer and use fucntion "QueueCreate" to initialize it. For example
uint8_t gQueueBuffer[sizeof(DataQueue) + 20 * sizeof(QueueDataType)] void main() { if ( QueueCreate(gQueueBuffer, sizeof(gQueueBuffer), NULL, NULL) == QUEUE_OK ) { // Create queue succ } else { // Create queue failed } }
Both of you think it's a very bad way to cast a buffer pointer to a struct pointer. But in some cases, for example communications protocol,
Offsets 0 1 2 3 4 STX ADDR DAT END CRC
I want to access data like this:
typedef __packed struct { uint8_t STX; uint8_t ADDR; uint8_t DAT; uint8_t END; uint8_t CRC; }Cmd, *pCmd; void ProcessCmd(uint8_t* cmdBuf, uint16_t len) { pCmd p = (pCmd)cmdBuf; assert_param(len == sizeof(Cmd)); if (p->STX == ..) { .... } .... }
If I should not cast such buffer pointer to struct pointer, what should I do to access communication data? Or like
if (cmdBuf[0] == ..) { .... }
?
I thought that user can define an global buffer and use fucntion "QueueCreate" to initialize it.
And why did you think that was in any way preferrable over having the user actually defining a buffer of type DataQueue?
Anyway, I hope you've now understood why that can't work. At the very least you'll have to ensure that buffer has sufficient alignment --- but unfortunately, there's not actually any good way you could make sure of that while letting the user allocate the storage and pass in a byte pointer. Either do your own allocating, or pick a pointer type with maximal alignment ... and good luck figuring out a portable way of determining what that might be.
Ever wondered what that language about "sufficiently aligned for any data type" in the standard specification of malloc() is about? Well, you've just run into it.
Just because you want to do something doesn't make it a good idea; it doesn't even make it possible. You have some reading-up to do on the topic of "serialization".
This is the #1 train of thought that gets people to believe they should use "packed" all over their in their programs, like you do: typedef __packed struct And in the vast majority of cases, it's just plain dumb wrong.
Since you already have a fixed data type for the elements, you could do:
enum { NUM_ENTRIES = 20, NUM_OTHER_ENTRIES = 40, }; Queue my_queue; QueueEntry my_entries[NUM_ENTRIES]; Queue my_other_queue; QueueEntry my_other_entries[NUM_OTHER_ENTRIES]; void init_queue(Queue *q,QueueEntry entries[],unsigned num_entries) { QueueEntries *p; unsigned i; memset(q,0,sizeof(*q)); q->first = NULL; q->last = NULL; q->num_entries = num_entries; // Chain together all entries in a list of unused. q->free = p = entries; for (i = 1; i < num_entries; i++,p++) { p->next = p+1; } p->next = NULL; ... } void main() { init_queue(&my_queue,my_entries,NUM_ENTRIES); init_queue(&my_other_queue,my_other_entries,NUM_OTHER_ENTRIES); ... }
Two statically allocated queues of different length but with correct align.
Thanks to Hans-Bernhard Broeker and Per Westermark. I got it.