Editor's note: In this first in a series of articles on problems related to C and C programming on embedded processors, Colin Walls of Mentor Embedded outlines an approach whereby developers can code low level routines easily using binary.
Embedded software developers commonly need to think about individual bits when working with CPU or peripheral device control registers. High level languages like C do not accommodate developers' needs well in this respect. This article outlines an approach whereby developers can code low level routines using binary in a natural, intuitive way.
Choice of Programming Language
I have a small collection of old computer manuals, mostly on software topics. A number are about specific programming languages. I just counted and 14 different languages are represented (not counting assembly). But which language would I naturally turn to for a programming job? The answer is C, or, at a push, C++. Part of the reason for this choice is the same as my motivation for working with embedded software – I like to be “in touch” with the machine that I am programming. But for the most part C, as a high level language, does not have the power and flexibility of assembly language.
Low level programming requires coding that deals with bits in machine registers – typically the control/status registers of the CPU or peripheral devices. Sometimes this may require access to a single bit; other times it may be arbitrary groups of 2, 3, 4 or more bits. Clearly, a means to describe and recognize bit patterns is needed.
Structures and Bit Fields
The C language was not designed for embedded programming. It was first implemented before microprocessors were even invented. However, it is a remarkably good fit for embedded programming needs. Access to register bit fields is almost fully supported in C. It is possible to access specific memory addresses; the data at that location may be organized in a variety of ways, including arbitrary bit fields. For example:
unsigned a : 3;
unsigned b : 1;
unsigned c : 2;
unsigned d : 1;
unsigned e : 1;
represents an 8-bit register with five bit fields of size 3, 1, 2, 1 and 1 respectively. To set bits 1 and 4 would need only two assignments, thus:
mydev.b = 1;
mydev.d = 1;
This code might work, but we cannot be certain of this because the C language standards do not dictate how the allocation of bit fields across the word is organized. So we might get lucky if they are sequentially allocated from the top of the byte, which is what this example assumes. However, this is entirely compiler dependent, and the code generated by another compiler (or even the same compiler with different optimizations) may not work at all.
It is an accident of evolution that we have five digits on each hand and, hence, a counting system on a base of 10. Of course, a bit pattern, which is essentially a binary number, may be represented by a decimal value, but this is not very intuitive. For example, it is far from obvious [to me] that the value 18 represents a register with bits 4 and 1 set to 1 and everything else set to 0. Clearly something better is needed.
An obvious approach is to simply use a number base that does make the bit pattern apparent. When I started out with programming, I used DEC mini-computers. First was a PDP-8, which had 12-bit words, so octal (base 8) was a good choice. Later I moved on to a PDP-11, which had 16-bit words, but DEC still continued to use octal. On the one hand, this was odd, as a 16-bit number needs 6 octal digits, the most significant one only taking the values 0 or 1. But there were two advantages: the top bit was the sign bit, but, more importantly, the instruction set of the CPU used 3-bit fields, so octal made instructions clear to the educated eye. Later, with the advent of 32-bit VAX computers, DEC came into line and started using hexadecimal.
Although I have been using hex for over 30 years, I still do not intuitively see the bit patterns. For example, seeing “0x0C” does not immediately translate to “00001100″ in my mind, whereas 014 in octal is quite apparent. Clearly, the C language is a compromise when it comes to describing bit fields.
In an ideal world, an embedded software programming language would include the capability to express values in binary. There is a simple way to add this to C (in effect). All that is needed is a binary.h file, which contains 256 lines like this:
#define b00000000 ((unsigned char) 0x00)
#define b00000001 ((unsigned char) 0x01)
#define b00000010 ((unsigned char) 0x02)
If you prefer, an upper case “B” might be used – it is a matter of personal preference. You can use these constants anywhere that an integer constant might be acceptable. Please email me if you would like a copy of the binary.h file.
More Than 8 Bits
Many modern processors and peripheral devices have 16- or 32-bit control registers. Although a complete set of binary constants for 16 bits could be created, the file would have over 65,000 lines, which is excessive. Addressing 32 bits this way is not practical.
A simple approach is to define macros to build 16- or 32-bit constants from the 8-bit ones, like this:
#define BIN16(hi,lo) (((hi)<<8) | (lo))
A value may be defined thus:
which would yield a constant of value 0x0ff0.
Of course, creating the equivalent BIN32() is quite straightforward.
Colin Walls has over thirty years experience in the electronics industry, largely dedicated to embedded software. A frequent presenter at conferences and seminars and author of numerous technical articles and two books on embedded software, Colin is an embedded software technologist in the Mentor Graphics Embedded Software Divisio. He is based in the UK. His regular blog is located at: blogs.mentor.com/colinwalls. He can be reached by email at email@example.com.