I'm trying to combine two typedef unions of a GPIO port of an ARM SoC into one, and address pointers into one. Currently, I have something which looks like this:
.h file:
//GPIO00 port
typedef union {
struct {
uint32_t GPIO000:1;
uint32_t GPIO001:1;
...
uint32_t GPIO0017:1;
};
struct {
uint32_t w:18;
};
} __GPIO00portbits_t;
volatile __GPIO00portbits_t * PTR_GPIO00portbits;
#define GPIO00portbits (*PTR_GPIO00portbits)
//GPIO01 port
typedef union {
struct {
uint32_t GPIO010:1;
uint32_t GPIO011:1;
...
uint32_t GPIO0117:1;
};
struct {
uint32_t w:18;
};
} __GPIO01portbits_t;
volatile __GPIO01portbits_t * PTR_GPIO01portbits;
#define GPIO01portbits (*PTR_GPIO01portbits)
.c file:
//GPIO 00 port
volatile __GPIO00portbits_t * PTR_GPIO00portbits = (__GPIO00portbits_t *) (AXIBRIDGE_BASE_ADDR + GPIO_00_BASE);
//GPIO 01 port
volatile __GPIO01portbits_t * PTR_GPIO01portbits = (__GPIO01portbits_t *) (AXIBRIDGE_BASE_ADDR + GPIO_01_BASE);
}
I can use this to control GPIO ports of the ARM SoC. I.e. I can control a single pin of GPIO00 by changing GPIO00portbits.GPIO00x. It works the same for GPIO01.
In reality, GPIO00 and GPIO01 are actually one port called GPIO0, where GPIO00 is pin 0-17 and GPIO01 is pin 18-35, so I would also like to combine GPIO00 and GPIO01 into one stuct which can be controlled by changing GPIO0portbits.GPIO0x.
So I would like to have something like this:
typedef union {
struct {
uint64_t GPIO00:1 = GPIO00portbits.GPIO000;
uint64_t GPIO01:1 = GPIO00portbits.GPIO001;
...
uint64_t GPIO035:1 = GPIO01portbits.GPIO0117;
};
struct {
uint32_t w:36;
};
} __GPIO0portbits_t;
How can I do this?
Thank you in advance.
Data types generally
You have defined two distinct types, __GPIO00portbits_t and __GPIO01portbits_t, with identical structure and closely related use. This is pointless, and it may even get in your way. I would probably do this, instead:
typedef union {
struct {
uint32_t GPIO0:1;
uint32_t GPIO1:1;
...
uint32_t GPIO17:1;
};
uint32_t w:18;
} __GPIOhalfportbits_t;
extern volatile __GPIOhalfportbits_t *PTR_GPIO00portbits;
#define GPIO00portbits (*PTR_GPIO00portbits)
extern volatile __GPIOhalfportbits_t * PTR_GPIO01portbits;
#define GPIO01portbits (*PTR_GPIO01portbits)
Note, by the way, that you need the externs if the header is going to be used in more than one .c file, and that in that case exactly one of those .c files should contain definitions you show.
Your specific request
I would also like to combine GPIO00 and GPIO01 into one stuct which can be controlled by changing GPIO0portbits.GPIO0x
It seems like you may not be maintaining the appropriate mental distinction between objects and their data types. That would explain your odd duplication of data types, and also the way you describe what you're looking for. If you want to be able to have the option to treat the data as either a full 36 bits or two 18-bit halves, then you could imagine continuing the above with something like this:
// XXX: see below
typedef union {
struct {
__GPIOhalfportbits_t group0;
__GPIOhalfportbits_t group1;
};
struct {
uint32_t GPIO0:1;
uint32_t GPIO1:1;
...
uint32_t GPIO35:1;
};
uint64_t w:36; // uint32_t isn't wide enough
} __GPIOportbits_t;
In principle, then, you might access an object of that type either by directly accessing the bits ...
__GPIOportbits_t portbits;
// ...
if (portbits.GPIO23) {
// ...
}
... or via the half-port pieces ...
if (portbits.group1.GPIO5) {
// ...
}
Something like that might work under different circumstances, but in your case, this will not work. The problem is that the number of bits in your half-port pieces is not a multiple of the number of bits in a char (8 on your hardware). The size of char is the unit in which object sizes are measured, and, accordingly, the finest possible granularity for addresses.
That means that the size of my __GPIOhalfportbits_t and your __GPIO00portbits_t and __GPIO01portbits_t is at least 24 bits, not 18 bits. Therefore, if you lay two of them out one after the other then the bitfields cannot be laid out as a contiguous 36-bit range starting at the beginning of the object. There are at least 6 (padding) bits of the first object that need to go somewhere before the bits of the second half-port object.
For substantially the same reason, there are no pointer tricks that can accomplish what you're after, either. If you have a region of 36 contiguous bits then the second half does not start on an addressible boundary, so you cannot form a pointer to it.
On the other hand, if the two halves are not contiguous in the first place, then you might be able to go with something like this:
typedef struct {
__GPIOhalfportbits_t group0;
__GPIOhalfportbits_t group1;
} __GPIOportbits_t;
You would have to pay attention to alignment of the two half-port pieces, but there is probably an implementation-specific way to get that right. Given that the underlying data (we have now assumed) is not presented as a contiguous span of 36 bits in the first place, forming a union with a 36-bit bitfield does not make sense. It might nevertheless be possible to use a union to map individual single-bit bitfields on top of that pair of structures by inserting explicit padding of the appropriate size, but you need to consider whether any of this is actually worth doing. In particular, see below.
Important other considerations
Bitfields are a tricky business in general, and C makes very few guarantees about their behavior -- many fewer than a lot of people suppose or expect. It is a particularly poor idea to use bitfields to write to hardware ports, because you cannot write fewer than CHAR_BIT bits at once, and if you're writing via a bitfield whose size is not a power-of-two multiple of CHAR_BIT then you will be writing additional bits as well, whose values are unspecified.
I generally recommend avoiding bitfields altogether, except possibly for usage of bitfields in C-language programming interfaces provided by the relevant hardware manufacturer, in a manner consistent with those interfaces' documentation.
Alternatives
You could conceivably come up with some wrapper macros for accessing the GPIO port in terms of two half ports, and even in terms of individual bits within those ports. But this answer is already long, and such a macro-centric approach would be a whole other story.
You can't do that as they live under different addresses in memory.
Using objects to access hardware registers is very inefficient. On this level of programming, you need to optimize code as much as possible.
https://godbolt.org/z/ncbr8o
YOu can only "combine" them by having additional object where you will read the data from that actual registers, and after changes save it to registers.
#include <stdint.h>
#define AXIBRIDGE_BASE_ADDR 0x12340000
#define GPIO_00_BASE 0x400
#define GPIO_01_BASE 0x800
//GPIO00 port
typedef union {
struct {
uint32_t GPIO000:1;
uint32_t GPIO001:1;
uint32_t GPIO002:1;
uint32_t GPIO003:1;
uint32_t GPIO004:1;
uint32_t GPIO005:1;
uint32_t GPIO006:1;
uint32_t GPIO007:1;
uint32_t GPIO008:1;
uint32_t GPIO009:1;
uint32_t GPIO010:1;
uint32_t GPIO011:1;
uint32_t GPIO012:1;
uint32_t GPIO013:1;
uint32_t GPIO014:1;
uint32_t GPIO015:1;
uint32_t GPIO016:1;
uint32_t GPIO017:1;
};
struct {
uint32_t w:18;
};
} __GPIO00portbits_t;
typedef union {
struct {
uint32_t GPIO000:1;
uint32_t GPIO001:1;
uint32_t GPIO002:1;
uint32_t GPIO003:1;
uint32_t GPIO004:1;
uint32_t GPIO005:1;
uint32_t GPIO006:1;
uint32_t GPIO007:1;
uint32_t GPIO008:1;
uint32_t GPIO009:1;
uint32_t GPIO010:1;
uint32_t GPIO011:1;
uint32_t GPIO012:1;
uint32_t GPIO013:1;
uint32_t GPIO014:1;
uint32_t GPIO015:1;
uint32_t GPIO016:1;
uint32_t GPIO017:1;
uint32_t GPIO100:1;
uint32_t GPIO101:1;
uint32_t GPIO102:1;
uint32_t GPIO103:1;
uint32_t GPIO104:1;
uint32_t GPIO105:1;
uint32_t GPIO106:1;
uint32_t GPIO107:1;
uint32_t GPIO108:1;
uint32_t GPIO109:1;
uint32_t GPIO110:1;
uint32_t GPIO111:1;
uint32_t GPIO112:1;
uint32_t GPIO113:1;
uint32_t GPIO114:1;
uint32_t GPIO115:1;
uint32_t GPIO116:1;
uint32_t GPIO117:1;
};
struct {
uint64_t GPIO1w:18;
uint64_t GPIO2w:18;
};
} __GPIO12portbits_t;
#define GPIO1 ((volatile __GPIO00portbits_t *)(AXIBRIDGE_BASE_ADDR + GPIO_00_BASE))
#define GPIO2 ((volatile __GPIO00portbits_t *)(AXIBRIDGE_BASE_ADDR + GPIO_01_BASE))
#define COMBINE() (&(__GPIO12portbits_t){.GPIO1w = GPIO1 -> w, .GPIO2w = GPIO2 -> w})
#define UPDATEGPIO(ptr) do{GPIO1 -> w = ptr -> GPIO1w; GPIO2 -> w = ptr -> GPIO2w;}while(0)
void foo()
{
__GPIO12portbits_t *ptr = COMBINE();
ptr -> GPIO014 = 1;
ptr -> GPIO110 = 1;
UPDATEGPIO(ptr);
}
void bar()
{
GPIO1 -> GPIO014 = 1;
GPIO2 -> GPIO010 = 1;
}
But it is very inefficient https://godbolt.org/z/jMsc7j
Related
I have a struct defined that is used for messages sent across two different interfaces. One of them requires 32-bit alignment, but I need to minimize the space they take. Essentially I'm trying to byte-pack the structs, i.e. #pragma pack(1) but ensure that the resulting struct is a multiple of 32-bits long. I'm using a gcc arm cross-compiler for a 32-bit M3 processor. What I think I want to do is something like this:
#pragma pack(1)
typedef struct my_type_t
{
uint32_t someVal;
uint8_t anotherVal;
uint8_t reserved[<??>];
}
#pragma pack()
where <??> ensures that the size of my_type_t is divisible by 4 bytes, but without hard-coding the padding size. I can do something like this:
#pragma pack(1)
typedef struct wrapper_t
{
my_type_t m;
uint8_t reserved[sizeof(my_type_t) + 4 - (sizeof(my_type_t) % 4)]
}
#pragma pack()
but I'd like to avoid that.
Ultimately what I need to do is copy this to a buffer that is 32-bit addressable, like:
static my_type_t t; //If it makes a difference, this will be declared statically in my C source file
...
memcpy(bufferPtr, (uint32_t*)&t, sizeof(t)) //or however I should do this
I've looked at the __attribute__((align(N))) attribute, which gives me the 32-bit aligned memory address for the struct, but it does not byte-pack it. I am confused about how (or if) this can be combined with pack(1).
My question is this:
What is the right way to declare these structs so that I can minimize their footprint in memory but that allows me to copy/set it in 4-byte increments with a unsigned 32-bit pointer? (There are a bunch of these types of arbitrary size and content). If my approach above of combining pack and padding is going about this totally wrong, I'll happily take alternatives.
Edit:
Some constraints: I do not have control over one of the interfaces. It is expecting byte-packed frames. The other side is 32-bit addressable memory mapped registers. I have 64k of memory for the entire executable, and I'm limited on the libraries etc. I can bring in. There is already a great deal of space optimization I've had to do.
The struct in this question was just to explain my question. I have numerous messages of varying content that this applies to.
I can't speak for the specific compiler and architecture you are using, but I would expect the following to be sufficient:
typedef struct {
uint32_t x;
uint8_t y;
} my_type_t;
The structure normally has the same alignment as its largest field, and that includes adding the necessary padding at the end.
my_type_t
+---------------+
| x |
+---+-----------+
| y | [padding] |
+---+-----------+
|<-- 32 bits -->|
Demo
This is done so the fields are properly aligned when you have an array of them.
my_type_t my_array[2];
my_array[1].x = 123; // Needs to be properly aligned.
The above assumes you have control over the order of the fields to get the best space efficiency, because it relies on the compiler aligning the individual fields. But those assumptions can be removed using GCC attributes.
typedef struct {
uint8_t x;
uint32_t y;
uint8_t z;
}
__attribute__((packed)) // Remove interfield padding.
__attribute__((aligned(4))) // Set alignment and add tail padding.
my_type_t;
This produces this:
my_type_t
+---+-----------+
| x | y
+---+---+-------+
| z | [pad] |
+---+---+-------+
|<-- 32 bits -->|
Demo
The packed attribute prevents padding from being added between fields, but aligning the structure to a 32-bit boundary forces the alignment you desire. This has the side effect of adding trailing padding so you can safely have an array of these structures.
As you use gcc you need to use one of the attributes.
Example + demo.
#define PACKED __attribute__((packed))
#define ALIGN(n) __attribute__((aligned(n)))
typedef struct
{
uint8_t anotherVal;
uint32_t someVal;
}PACKED my_type_t;
my_type_t t = {1, 5};
ALIGN(64) my_type_t t1 = {1, 5};
ALIGN(512) my_type_t t2 = {2, 6};
int main()
{
printf("%p, %p, %p", (void *)&t, (void *)&t1, (void *)&t2);
}
Result:
0x404400, 0x404440, 0x404600
https://godbolt.org/z/j9YjqzEYW
I suggest combining #pragma pack with alignas:
#include <stdalign.h>
#include <stdint.h>
typedef struct {
#pragma pack(1)
alignas(4) struct { // requires 2+1+2 bytes but is aligned to even 4:s
uint16_t someVal; // +0
uint8_t anotherVal; // +2
uint16_t foo; // +3 (would be 4 without packing)
};
#pragma pack()
} my_type_t;
The anonymous inside struct makes access easy as before:
int main() {
my_type_t y;
y.someVal = 10;
y.anotherVal = 'a';
y.foo = 20;
printf("%zu\n", (char*)&y.someVal - (char*)&y.someVal); // 0
printf("%zu\n", (char*)&y.anotherVal - (char*)&y.someVal); // 2
printf("%zu\n", (char*)&y.foo - (char*)&y.someVal); // 3
my_type_t x[2];
printf("%zu\n", (char*)&x[1] - (char*)&x[0]); // 8 bytes diff
}
If you'd like to be able to take the sizeof the actual data carrying part of my_type_t (to send it), you could name the inner struct (which makes accessing the fields a little more cumbersome):
#pragma pack(1)
typedef struct {
uint16_t someVal;
uint8_t anotherVal;
uint16_t foo;
} inner;
#pragma pack()
typedef struct {
alignas(4) inner i;
} my_type_t;
You'd now have to mention i to access the fields, but it has the benefit that you can take sizeof and get 5 (in this example):
int main() {
my_type_t y;
printf("%zu %zu\n", sizeof y, alignof(y)); // 8 4
printf("%zu\n", sizeof y.i); // 5 (the actual data)
}
To form a structure type that is aligned one must put the alignment attribute to the first member of the struct. It can be combined with the packed attribute.
typedef struct {
_Alignas(4) uint8_t anotherVal;
uint32_t someVal;
} __attribute__((packed)) my_type_t;
Exemplary usage with alignment exaggerated to 64 bytes.
#include <stdint.h>
#include <stdio.h>
typedef struct {
_Alignas(64) uint8_t anotherVal;
uint32_t someVal;
} __attribute__((packed)) my_type_t;
int main() {
my_type_t a, b;
printf("%zu %p\n", sizeof a, (void*)&a);
printf("%zu %p\n", sizeof b, (void*)&b);
}
prints:
64 0x7ffff26caf80
64 0x7ffff26cafc0
I want to calculate the CRC value of some data in STM32 micro controller.
The HAL function to calculate the CRC has the following footprint:
uint32_t HAL_CRC_Calculate(CRC_HandleTypeDef *hcrc, uint32_t pBuffer[], uint32_t BufferLength);
My data are stored in a struct:
struct caldata_tag {
float K_P_Htng;
uint16_t K_I_Htng;
uint16_t K_D_Htng;
uint16_t K_P_Coolg; } caldata;
Who is the safest and appropriate way to pass the struct to the HAL_CRC_Calculate() function?
I am thinking about this :
#define U32BUFFERSIZE sizeof(struct caldata_tag)/sizeof(uint32_t)
uint32_t buffer[U32BUFFERSIZE];
uint32_t crcValue;
/* calculate the crc value of the data */
memcpy(buffer,&localStruct,U32BUFFERSIZE);
crcValue = HAL_CRC_Calculate(&CrcHandle,buffer,U32BUFFERSIZE);
but I am thinking that is an ugly way, could you tell me if it is ok? OR if you have a better idea?
Who is the safest and appropriate way to pass the struct to the HAL_CRC_Calculate()function?
Challenges:
HAL_CRC_Calculate() apparently wants to calculate the CRC based on multiples of uint32_t.
The size of struct caldata_tag may not be a multiple of the size of uint32_t.
struct caldata_tag may contain padding of an unknown state in caldata.
Use a union of struct caldata_tag and a large enough uint32_t array. Zero it, copy the members and then calculate the CRC.
I am thinking that is an ugly way, could you tell me if it is ok? OR if you have a better idea?
Form a helper function.
// Find the quotient of sizeof caldata_tag / sizeof(uint32_t), rounded up
#define U32BUFFERSIZE ((sizeof(struct caldata_tag) + sizeof(uint32_t) - 1)/sizeof(uint32_t))
uint32_t caldata_CRC(CRC_HandleTypeDef *hcrc, const struct caldata_tag *p) {
// u's size will be a multiple of sizeof uint32_t
union {
uint32_t u32[U32BUFFERSIZE];
struct caldata_tag tag;
} u = { {0} }; // zero every thing
// copy the members, not the padding
u.tag.K_P_Htng = p->K_P_Htng;
u.tag.K_I_Htng = p->K_I_Htng;
u.tag.K_D_Htng = p->K_D_Htng;
u.tag.K_P_Coolg = p->K_P_Coolg;
return HAL_CRC_Calculate(hcrc, u.u32, U32BUFFERSIZE);
}
Use
uint32_t crcValue = caldata_CRC(&CrcHandle, &caldata);
[Update]
Further research indicates that the BufferLength is a count of uint8_t, uint16_t, uint32_t depending on hcrc->InputDataFormat. OP has not provided that, yet if that can be set to uint8_t. then code only needs to worry about padding in struct caldata.
#define U8BUFFERSIZE sizeof(struct caldata_tag)
uint32_t caldata8_CRC(CRC_HandleTypeDef *hcrc, const struct caldata_tag *p) {
// u's size will be a multiple of sizeof uint32_t
union {
uint32_t u32[U32BUFFERSIZE];
struct caldata_tag tag;
} u = { {0} }; // zero every thing
// copy the members, not the padding
u.tag.K_P_Htng = p->K_P_Htng;
u.tag.K_I_Htng = p->K_I_Htng;
u.tag.K_D_Htng = p->K_D_Htng;
u.tag.K_P_Coolg = p->K_P_Coolg;
return HAL_CRC_Calculate(hcrc, u.u32, U8BUFFERSIZE);
}
If the compiler allows __attribute__((__packed__)), #sephiroth answer is a good way to go.
You can use a pointer that points directly to the beginning of the struct, without having to use the support buffer:
uint32_t *p = (uint32_t*)&localStruct;
There are 2 problems with this:
The first one is that you might get unexpected results if the compiler is doing padding on the struct; you can solve this by adding the (packed) attribute to the struct to tell the compiler not to do any padding
struct __attribute__((__packed__)) caldata_tag {
//...
}
The other problem is that your structure size isn't a multiple of 32, so it can't be represented in an array of uint32_t without having 16 random bits at the end of the last element. The same goes for your example, bur i think in this case you are discarding the last element of buffer because U32BUFFERSIZE should be equal to 2, so you are ignoring K_P_Coolg and the 16 random bits beside it when calculating the crc.
My suggestion when working with crc and stuff like that is using 8 bit buffers instead of 32 bit ones, as it completely eliminates the latter problem.
No workaround is needed. According to the documentation for the function HAL_CRC_Calculate: "By default, the API expects a uint32_t pointer as input buffer parameter. Input buffer pointers with other types simply need to be cast in uint32_t and the API will internally adjust its input data processing based on the handle field hcrc->InputDataFormat."
So set the field correctly in the first parameter and you can pass a pointer to bytes to the function.
Consider this typedef:
#pragma pack(4)
typedef struct
{
uint8 dataArea0[11];
uint8 dataArea1[12];
uint8 dataArea2[13];
uint8 dataArea3[14];
} myStruct;
I have some non-2^n sized arrays that I'd like to use from other libs. From those libs these dataAreas can be cast as e.g., structs or whatever i need. The problem occurs when one of these struct members land on a non 4-byte aligned address AND contain data types that are not happy about their address alignment.
Therefore I'd like to force the alignment with the pack pragma, but this does not help (at least in the IAR compiler -- from the manual: Use this pragma directive to specify the maximum alignment of struct and union members.). I also tried to use the data_alignment pragma, but this seems to be for variables and not struct members.
Does anyone know a nice compiler trick to force the alignment of the struct members?
Quicklink to compiler manual for those interested: IAR AVR32 Compiler Ref
Edit:
I ended up using this as an alternative
#define ROUND_UP_NEXT_FOUR(x) ((x + 3) & ~0x03)
typedef struct
{
uint8 dataArea0[ROUND_UP_NEXT_FOUR(11)];
uint8 dataArea1[ROUND_UP_NEXT_FOUR(12)];
uint8 dataArea2[ROUND_UP_NEXT_FOUR(13)];
uint8 dataArea3[ROUND_UP_NEXT_FOUR(14)];
} myStruct;
In this way I'm sure that the padding will take place at a 4-aligned address.
Edit2:
An example of how this can go wrong:
struct otherStruct
{
uint16 dataBuf0;
uint32 dataBuf1;
uint32 dataBuf2;
uint32 dataBuf3;
uint32 dataBuf4[10];
};
myStruct* myStructInstance = 0x00000000; //some address
//address of this is 0x0B
struct otherStruct* oS = (struct otherStruct*) myStructInstance.dataArea1;
//we assign to a 2 byte variable that is
//located at address that is not 2 byte aligned -> error!
os->dataBuf0 = 10;
In this case we get a runtime error (worst (or best?) case, crash).
Unfortunately the IAR AVR32 compiler does not support the _Alignas keyword. However, when IAR language extensions are enabled it supports anonymous unions and this can be used to force the alignment of individual fields of a struct. The trick is that the alignment of a union is the strictest (largest) alignment of any of its fields. Thus, by wrapping each field dataArea? in an anonymous union with a dummy field with 32-bit alignment it is possible to force the alignment of each dataArea? field to 32-bit. An example is shown below. It include both raw anonymous-union declarations as well as macro-magic to simplify declaration when the number of fields is large.
#include <stdint.h>
#define GLUE_B(x,y) x##y
#define GLUE(x,y) GLUE_B(x,y)
#define ALIGNED(FIELD, ALIGN_TYPE) union { FIELD; ALIGN_TYPE GLUE(a,__LINE__); }
#define ALIGNED32(FIELD) ALIGNED(FIELD, uint32_t)
typedef struct
{
ALIGNED(uint8_t dataArea0[11], uint32_t);
ALIGNED32(uint8_t dataArea1[12]);
union { uint8_t dataArea2[13]; uint32_t a2;};
union { uint8_t dataArea3[12]; uint32_t a3;};
} myStruct;
I'm using packed structure for communication using direct DMA access, and here is my test code:
// structure for communication buf 1
typedef __packed struct _test1
{
uint8_t a;
uint32_t b;
uint16_t c;
uint16_t d;
uint32_t e;
} test1;
// structure for communication buf 2
.
.
.
// structure for communication buf 3
.
.
.
// structure for communication buf set
typedef __packed struct _test2
{
uint8_t dump[3];
test1 t;
// may have many other packed structure for communication buf
} test2;
#pragma anon_unions
typedef struct _test3
{
union
{
uint32_t buf[4];
__packed struct
{
__packed uint8_t dump[3];
test1 t;
};
};
} test3;
test1 t1;
test2 t2;
test3 t3;
size of these structures are
sizeof(t1) = 13
sizeof(t2) = 16
sizeof(t3) = 16
if I want to access variable b, for not effecting performance, read/write memory content with aligned access is needed, with calculated offset by hand
t3.buf[1]
but I cannot read/write variables in structure without using unaligned accesses
t2.t.b
t3.t.b
so I defined structures like the following code, packed only variable a
typedef struct _test4
{
__packed uint8_t a;
uint32_t b;
uint16_t c;
uint16_t d;
uint32_t e;
} test4;
typedef struct _test5
{
__packed uint8_t dump[3];
test4 t;
} test5;
test4 t4;
test5 t5;
although access of all element in structure is aligned, but padding is inserted either
sizeof(t4) = 16
sizeof(t5) = 20
so how can I define packed structures, and access single variable in it without using unaligned access(except a)?
thanks a lot for helping
Your question introduces two problems under the umbrella of one:
Communication between components and/or devices; this may or may not have the same underlying representation of structures and integers, hence your use of the non-portable __packed attribute.
Performance of access, biased by alignment and/or data size; on one hand the compiler aligns data to fall in line with the bus, yet on the other hand that data might occupy too much space in your cache.
One of these is the actual problem you want to solve, X, and the other the Y in your XY problem. Please avoid asking XY problems in the future.
Have you considered how to guarantee that uint16_t and uint32_t will be big endian or little endian, based on your requirements? You need to specify that, if you care about portability. I care about portability, so that's what my answer will focus on. You may also notice how optimal efficiency will be obtained, too. Nonetheless, to make this portable:
You should be serialising your data using serialisation functions to convert each member of your structure into sequences of bytes by division and modulo (or left shift and binary and) operations.
Similarly, you should be deserialising your data by inverse operations multiplication and addition (or right shift and binary or).
As an example, here's some code showing both little endian and big endian for serialising and deserialising test1:
typedef /*__packed*/ struct test1
{
uint32_t b;
uint32_t e;
uint16_t c;
uint16_t d;
uint8_t a;
} test1;
void serialise_test1(test1 *destination, void *source) {
uint8_t *s = source;
destination->a = s[0];
destination->b = s[1] * 0x01000000UL
+ s[2] * 0x00010000UL
+ s[3] * 0x00000100UL
+ s[4]; /* big endian */
destination->c = s[5] * 0x0100U
+ s[6]; /* big endian */
destination->d = s[7]
+ s[8] * 0x0100U; /* little endian */
destination->e = s[9]
+ s[10] * 0x00000100UL
+ s[11] * 0x00010000UL
+ s[12] * 0x01000000UL; /* little endian */
}
void deserialise_test1(void *destination, test1 *source) {
uint8_t temp[] = { source->a
, source->b >> 24, source->b >> 16
, source->b >> 8, source->b
, source->c >> 8, source->c
, source->d, source->d >> 8
, source->d >> 16, source->b >> 24 };
memcpy(destination, temp, sizeof temp);
}
You may notice that I removed the __packed attribute and rearranged the members, so that the larger members precede (i.e. come before) the smaller; this is likely to reduce padding significantly. The functions allow you to convert between an array of uint8_t (which you send to/receive from the wire, or DMA, or whatnot) and your test1 structure, so this code is much more portable. You benefit from the guarantees this code provides regarding the structure of your protocol, where-as before it was at the whim of the implementation, and two devices using two different implementations might have disagreed about the internal representation of integers for example.
You could hard code all the indexes like
typedef __packed struct _test1
{
uint8_t a;
uint32_t b;
uint16_t c;
uint16_t d;
uint32_t e;
} test1;
enum
{
a = 0,
b = 1,
c = 5,
d = 7,
e = 9,
};
test1 t1 = {1,2,3,4};//not sure if init lists work for packed values
printf("%u", *(uint32_t*)((uint8_t*)&t1 + b));
Or offsetof can be used like this
printf("%u", *(uint32_t*)((uint8_t*)&t1 + offsetof(test1, b)));
I am struggling trying to come up with a clean way to redefine some register bitfields to be usable on a chip I am working with.
For example, this is what one of the CAN configuration registers is defined as:
extern volatile near unsigned char BRGCON1;
extern volatile near struct {
unsigned BRP0:1;
unsigned BRP1:1;
unsigned BRP2:1;
unsigned BRP3:1;
unsigned BRP4:1;
unsigned BRP5:1;
unsigned SJW0:1;
unsigned SJW1:1;
} BRGCON1bits;
Neither of these definitions is all that helpful, as I need to assign the BRP and SJW like the following:
struct
{
unsigned BRP:6;
unsigned SJW:2;
} GoodBRGbits;
Here are two attempts that I have made:
Attempt #1:
union
{
byte Value;
struct
{
unsigned Prescaler:6;
unsigned SynchronizedJumpWidth:2;
};
} BaudRateConfig1 = {NULL};
BaudRateConfig1.Prescaler = 5;
BRGCON1 = BaudRateConfig1.Value;
Attempt #2:
static volatile near struct
{
unsigned Prescaler:6;
unsigned SynchronizedJumpWidth:2;
} *BaudRateConfig1 = (volatile near void*)&BRGCON1;
BaudRateConfig1->Prescaler = 5;
Are there any "cleaner" ways to accomplish what I am trying to do? Also I am slightly annoyed about the volatile near casting in Attempt #2. Is it necessary to specify a variable is near?
Personally, I try to avoid using using bit fields for portability reasons. Instead, I tend to use bit masks so that I can explicitly control which bits are used.
For example (assuming the bit order is correct) ...
#define BRP0 0x80
#define BRP1 0x40
#define BRP2 0x20
#define BRP3 0x10
#define BRP4 0x08
#define BRP5 0x04
#define SJW0 0x02
#define SJW1 0x01
Masks can then be generated as appropriate and values assigned or read or tested. Better names for the macros can be picked by you.
Hope this helps.
I suggest that you dont mix up the bitfield declaration with the adressing of the hardware register.
Your union/struct declares how the bitfields are arranged, then you specify addressing and access restrictions when declaring a pointer to such a structure.
// foo.h
// Declare struct, declare pointer to hw reg
struct com_setup_t {
unsigned BRP:6;
unsigned SJW:2;
};
extern volatile near struct com_setup_t *BaudRateConfig1;
// foo.c
// Initialise pointer
volatile near struct com_setup_t *BaudRateConfig1 =
(volatile near struct com_setup_t *)0xfff...;
// access hw reg
foo() {
...
BaudRateConfig1->BRP = 3;
...
}
Regarding near/far I assume that the default is near unless far is specified, unless you can set the default pointer size to far using compiler switches.