On Mon, 14 Jun 2004 11:00:35 -0400, Jim Thomas <jthomas@bittware.com> wrote:>Anton Erasmus wrote: >> One portable way I have seen to force C to use a specific size is the >> following: >> >> typedef struct {unsigned data:8;} BYTE; >> >> The only problem is that if you define a variable as >> >> BYTE foo; >> >> you have to use it with following syntax >> >> foo.data=20; > >Unfortunately, that's not the only problem. The C standard does not >dictate the order of bits in a bit field. The compiler is at liberty to >put the first N bits in the ls-bits or the ms-bits, so this: > >typedef struct >{ > byte_a : 8; > byte_b : 8; >} > >Can have byte_a in the upper 8 bits or in the lower 8-bits. If you're >exchanging this data between two machines that use two different >compilers, you have to make sure they speak the same bit field lingo, or >you have to jump through hoops to re-define them based on how they get >packed. I think "portable" and "jump through hoops" could be considered >mutually exclusive.Getting data to have the same memory layout in different compilers and on different architectures are a total different ballgame. The method I proposed only handles the size issue. To handle the actual memory layout one would have to use #pragmas or some other non-portable directives to do this. I do agree that this is inherantly non-portable. Regards Anton Erasmus
Octets with non-8 bit bytes...
Started by ●June 10, 2004
Reply by ●June 14, 20042004-06-14
Reply by ●June 15, 20042004-06-15
Hi Alex, here's how I would handle this issue ... Alex Sanks wrote:> ... > I'm working with some firmware drivers which are intended to be as > portable as possible.Being aware that complete portability across very different platforms will be not achievable, I'd sstart with declaring different environment with #define PLATFORM_1 etc.> DataBc. data types might differ (as you indicate) whereever they're received/transmitted/transformed, use 'typedef' or similar methods to abstract from machine/compiler specific types wherever possible. Your u8, u32 types are a good start.> movesData doesn't move by itself. Instead, you have to move data. How, might differ depending on the peripheral (USB controller,...) and/or on the machine/platform/compiler. Thus, again you might use #define/#ifdef to handle data movements. This means, install generic handler routines to move data, which do all the machine/platform/compiler/periphery dependent stuff, and which provide a generic interface. Then you can keep the rest of the software (almost ?) portable. If you want some fairly good examples of how to do such things, you might want to read the source code files of the Linux OS. Especially of interest for your work might be the book "Linux device drivers" ,Alessandro Rubini, which gives a lot of good hints.> thru a switchable 8- or 16-bit > data bus chip (a USB device controller specifically). > Performance is critical so 16-bit is pretty much necessary.I guess you mean Performance='fast execution'. So, you might want to do things in compile time whereever possible. One example to do a thing in compile time instead of runtime, is this: u16 x; u8 y; x=1234; y = x & 0xff; /* this is the runtime approach */ u8 y; union { u16 x16; u8 x8_1; u8 x8_2; } ux; ux.x16=1234; y=ux.x8_2; /* this is the compile time approach */ Though this approach is highly machine/compiler dependent, it may save you lot of time, thus giving you performance gain. Probably, you'll have to implement such a thing individually for every new environment. Which means, that the scope of such critical things should be as small as possible.> Following that example, > let's look at the USB mass storage class. You get commands from > the host in 31 octetI count only 30 ...?!> command wrappers that look like this (endian > issues aside...): > > typedef struct > { > u32 Signature; > u32 Tag; > u32 TransferLength; > u8 Flags; > u8 Lun; > u8 CommandLength; > u8 Command[15]; > } Cbw; > > If I have 8 bit data types that's easy enough to get and deal > with. But right now I'm working with a TMS320C55x variant with > nothing > smaller than 16-bit data types.Approach depends on where you need your performance and where the conversions are cheapest. One might be to define a 16bit type which holds two octets: typedef union { u16 the16wide; u16 the8wideH:8; u16 the8wideL:8; } my16; .... my32; Based on this you could define your structure with typedef struct { my32 Signature; my16 Tag01; my16 Tag23; ... } Cbw; For convenience, you might define: #define Signature0 Signature.the8wide0 ... #define Tag0 Tag01.the8wideH #define Tag1 Tag01.the8wideL Then you can access the data as usual with Cbw.Tag1 or with Cbw.Signature or however you want.> > Ugly.Certainly, that's ugly. Since I did some device drivers which had to be "a little bit" portable, I'd say that you'll not be able to avoid such ugly things. With a little effort, you can write things so that they are readable without chance to misunderstand what is meant. This would be a great thing and certainly highly appreciated by those people who have to modify your code later. I'd try to hide all the ugly things in extra files and keep them as small as possible. Try to keep the rest neat, instead.> I'd really like to avoid that...You're free to like it or dislike it. That's life that you don't always get what you like...> ... > I don't care if everything's stored locally inefficiently.If so, why not just define a typedef u16 BYTE; define the structure as you wrote andignore the unused bytes. However, to avoid that some other function works with corrupted data, or corrupts them, I'd write access functions which are the only to work on the structure. Hide the data structure in a .c-file together with the manipulator functions, and export in the .h-file not the structure, but the manipulator functions together with an abstract, generic container type. This could look like: ---------- file.h ------------ typedef unsigned short u16; typedef unsigned long u32; typedef struct { u32 signature; u16 tag; .... } genericCBW; int read_data(genericCBW * returnsDataHere); int write_data(genericCBW * needsDataHere); --------------------------- --------- file.c ------------ #define PLATFORM all the ugly stuff you can't avoid... all your conversions necessary, all the real read/write accesses and the definitions for all the real data storage elements. (your real Cbw structures) int read_data(genericCBW * returnsDataHere) { returnsDataHere->signature = functionwhichknowshowtogetSignature(); ... } int write_data(genericCBW * needsDataHere) { functionwhichknowshowtowriteSignature(needsDataHere->signature); } ---------------------------> > I'm making progress getting things to work, but it's getting ugly > so I was curious how people deal with this in real life. > > Thanks for whatever guidance you can provide, > alexBernhard






