-
Notifications
You must be signed in to change notification settings - Fork 4
Raspberry Pi Hardware
Prev: Pointers
When looking at the integer types, the majority have a width in bits specified as part of their type name:
Int8
Int16
Int32
Int64
Without getting too far ahead of ourselves, when programming the hardware of the Raspberry Pi, we know that we are going to be dealing with hardware registers.
We also those registers will be the word size of the platform, and that on the 32-bit ARM architecture of the Raspberry Pi, that word size will be 32-bits (4 bytes).
This is also true if you choose to use a 64-bit ARM port on the Raspberry Pi 2 or 3, since the architecture does not otherwise change the hardware interface. For this reason, those ports retain the basic word size of 32-bits.
So we might assume that Int32
would be the correct type to pick, and it's certainly a valid choice, however I argue that it's the wrong one.
These types are ideal when portability is a concern, especially when communicating between different system types over a network. But when interacting with the local system, using them feels like "premature porting."
Swift provides us with an integer type that has the same width as the word size of the platform:
Int
When reading through the Raspberry Pi data sheets, it's pretty clear that all hardware registers are handled as 32-bit words. These words are going to be used as bit fields, structure fields, values, and addresses. But what type should we use to reference them? Swift provides us four:
- Int
- UInt
- Int32
- UInt32
And we mentioned above, once we pick a type, we cannot arbitrarily convert it between the different types. Now it might be tempting to pick the fixed-width types, and even the unsigned type, but I'm going to argue that the correct type to use is Int
.
Picking a fixed-width type feels like "premature porting." Yes it's true that the registers are 32-bits wide,, but on the ARM microprocessor in the Raspberry Pi, integers and pointers are also 32-bits wide.
We simply don't have a 64-bit Pi to read the datasheet from, but we can make some guesses. That 64-bit processor will be almost certainly using 64-bit memory addresses, which means those registers would need to be 64-bits wide to fit them. But if we premature ported and declared them as Int32
they wouldn't work anymore, while Int
might just work fine.
Since on our platform Int
is the correct width, we should just use that. Porting can come later, if it ever becomes necessary.
The difference between the signed Int
type, and the unsigned UInt
type is just that; one allows negative numbers, and the other does not. Otherwise they are the same width, and have the same stride and alignment.
This matters when we store numerical values and want to be able to perform mathematical operations on them. The Int
type will overflow earlier than the UInt
type, and thus won't be able to store as wide range of values.
But when we're not doing that kind of thing, the distinction doesn't matter, since both types can store the same number of distinct values—it's just the mathematical range that differs.
We can still set the 31st bit of an Int
, the difference is just that should be use it mathematically, it will be interpreted as a negative number.
Since we're storing addresses, bit fields, structures, etc. and even distinct values, none of these need mathematical operations performed, thus Int
will work just as well.
The final argument for why Int
is better is simply the following:
let someValue = 1 << 4
With the lack of further typing, Swift will infer this value to be of Int
type. Since integer types are not directly compatible, we would then be unable to use that constant without continual casting, re-interpreting, truncating, etc. And that would leave us very prone to bugs.
But just using Int
, we can avoid all of these.