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
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.
I want to access data like this:
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.