Skip site navigation (1)Skip section navigation (2)
Date:      Sun, 6 Apr 1997 12:24:50 -0700 (MST)
From:      Terry Lambert <terry@lambert.org>
To:        hm@kts.org
Cc:        freebsd-hackers@freebsd.org
Subject:   Re: How to declare device driver variables and data structures ?
Message-ID:  <199704061924.MAA28524@phaeton.artisoft.com>
In-Reply-To: <m0wDonp-00002GC@ernie.kts.org> from "Hellmuth Michaelis" at Apr 6, 97 12:04:01 pm

next in thread | previous in thread | raw e-mail | index | archive | help
> Inside a device driver or a piece of kernel software, how should one decleare
> the size of variables (if one has the choice):
> 
> i.e. if i know a variable can only have values 0 ... 32, shall i make it
> an unsigned char ? Is it slower to make such variables an unsigned int
> or an unsigned short ? What are the space vs. speed considerations ?

As far as types go, "int" is faster.  Lattice and several other 68k
compilers got "int" wrong, and made it the register size instead of
the bus transfer size, and made 68k "int" slow.  The default for Aztec
and several other Macintosh and Amiga compilers (other than Lattice)
was 16 bits, the size of a datum that could be transferred in a single
bus fetch.

All that said, don't use int unless your ranges are short sized or
smaller (int is guaranteed to be as large or larger than short).

Of course, there's no guarantee that they won't implement "sort" as
less than 16 bits...


> i.e. if i know a variable can only have values 0 ... 2048, shall i make it
> an unsigned short ? Is it slower to make such variables an unsigned int ?

I would recommend that you use the defined sized types (int8_t, int16_t,
int32_t) and the unsigned sized types (u_int8_t, u_int16_t, u_int32_t)
for devices with defined size requirements.  This insures that moving
from compiler to compiler and architecture to architecture, your code
will still run (even if it isn't as effecient as it might be).

One could argue that for the kernel, sized types < sizeof(int) should
be implemented with int, for a lot of reasons, including access on
alignment boundries being guaranteed for int, but not for anything
else (aligned access requirements are a bus attribute, and int is
supposed to be sized based on the bus transfer).  For systems where
there is more strict alignment requirements, the compiler should take
care of it for you (either by generating extraction instructions, or,
preferrably for speed, promoting the size of int up).


> another i.e. for boolean variables, is it better to make them chars, shorts
> or ints ? signed or unsigned ?

Depends.  Again, "int" is easiest, and you need only one bit, so you
don't care about size, but are you sure that boolean will not have to
change to something else in a later rev of the code?

> What is the speed of unsigned variables vs. signed variables ?

Generally no difference on a two's complement machine.  Most machines
are two's complement.

The speed penalties for signed vs. unsigned variables come into play
when converting from one size of type to another.  In order for an 8 bit
minus one (0xff) to become a 16 bit minus one (0xffff), it must be
"sign extended" on conversion.

On some hardware, this is done by emitting code; on other hardware,
the processor can do it automatically as part of the access/load
cycles.

If you want it to be generally fast instead of fast on some hardware
and slow on others, use unsigned where possible... or avoid conversions
(many comparisons cause implicit sign extension to int, unless you
are very careful in your casting).


> What is with future ports to other architectures ?

Be careful with alignment and assumption of sizes of atomic types
that are not per-architecture typedef'ed to sized types.

If you use a sized type in a structure, the size will be invariant
across architectures (as long as the size is supported at all!  Beware
the 16 bit size!).

Actually, one thing I've wanted for some time now is to be able
to turn on the "unaligned access exception bit" on the Intel
platform to get the software into shape by flagging the accesses,
but not crippling the system while they are being fixed.  This would
mean trapping exception 17 (decimal) and setting bit 18 of EFlags
and bit 18 of Control Register 0 (unfortunately, it only works
against code running at priveledge level 3, so it won't help kernel
programmers).



> How does this all apply to variables being part of structures ?

For structures which overlay device data, the structure packing must
be such that the device data can overlay correctly.  In general, this
means setting the packing to 1 (using "#pragma pack(1)" in gcc) and
assuming a byte-for-byte alignment of data.  Since you still have
issues of access alignment, the actual structure may be padded seperately
from its packing to force the overall structure to an alignment boundry.

When you reference the data in the structure, you want to copy the
data if it's a data bus access using an alignment boundry granularity
for the copy (this is why the padding is there: so that the extra
"garbage" data doesn't stomp on anything).  If it's a memory bus
access (the device has mapped memory), you can just point the
structure at the data and access it normally, as long as the base
is at an alignment boundry.  This can be a problem for download code
for a smart card, since you may not have paid attention to alignment
boundries when you generated the mapped memory data on board the card,
and the system the card is plugged into now cares about that.

Technically, if you are writing Intel code, you can ignore the data
alignment; but if you do, your code will run slower and require more
bus accesses than if you had paid attention.


And that's about the end of my diatribe on alignment.  8-).


					Regards,
					Terry Lambert
					terry@lambert.org
---
Any opinions in this posting are my own and not those of my present
or previous employers.



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?199704061924.MAA28524>