Casting and Data Abstraction in C
Written by Kerry Crouse
When designing embedded systems, there exists the problem of accessing memory mapped registers. There are a number of methods which can be used to access memory mapped registers and setting up pointers to these locations is the most frequent method used to access these registers. Since I've been programming, I've come across several ways of specifying these registers: using the actual numeric address, using a variable containing the address, using a variable containing a pointer to the address, using a macro containing the address, using a macro containing a pointer to the address, and using a macro containing a dereferenced pointer to the address.
This is simply the address of a register with an appropriate casting. For instance, a byte location at hexadecimal address 1000 would be cast as:
This cast leaves us with a pointer to location 0x1000. To use this pointer, we must dereference the pointer using an additional ‘*' so we can actually assign data. The construct appears as:
The advantage of this approach is the visibility of the address and the speed of execution. Upon inspection we can see this is data.
A variable may be used to hold the address. This approach ends up very similar to the Numeric Address approach. For example:
*SerRcv = 86;
The advantages of the Variable storage approach is that the address can be modified at any time, the address can be changed in one place, and a symbolic name can be used for the location. The down side of this technique is that the variable location must be calculated for each access which begs the question: how often, if ever, will the address change at run time.
This is similar to the Numeric address in a macro wrapper. Using the example above, the code appears as:
An assignment appears as:
Advantages are the address can be changed at one place at compile time and a symbolic name is used. The address, however, is used in a cumbersome way.
Macro Address Pointer
This is the same as the Numeric address with a macro wrapper. Using the example above, the code looks like this:
An assignment looks like this:
The advantages are that a symbolic name is used amd the address can be changed in only one place at compile time.
Dereferenced Macro Address Pointer
This is almost the same as the Macro address pointer. Using the example above, the code looks like this:
An assignment looks like this:
The advantages are the address can be changed at one place at compile time, a representative symbolic name is used, and the location looks and behaves like a built-in register.
Using Dereferenced Macro Address Pointers
If the pointers are part of a larger I/O structure, the whole structure may be embedded into a macro representation of the I/O architecture.
Other Dereferenced Macro Address PointersNot only can base address types be used as address pointers, but structures can also be used. An example follows:
An example of a bitfield looks like this:
The serial status register is used to check the serial receiver bit. This check won't work if the bit resets after it is read.
The method of addressing a fixed memory location should be efficient from a performance standpoint, efficient from a speed perspective, easy for programmers to understand, and easy to change without breaking the code. All of the address calculations are done in a macro, therefore the performance is excellent. There are no variables involved, so the speed is fast. Due to the symbolic name, the function is easy for programmers to understand. Lastly, since the address calculations drop out of a macro, register and even board level locations can be changed easily. A macro using dereferenced cast pointer constants can provide all of these attributes.