Lecture 10
Lecture 10
Lecture 10:
Hardware interfacing and bit-level
programming
Burns/Wellings ch 15
1
Basics
• The bit:
- 0 or 1
- high or low voltage
• The byte:
- a collection of 8 bits
• The word:
- 32 bits on modern machines
- 16 bits on smaller architectures
- 64-bit machines are coming
2
Binary numbers
• There are 256 different possibilities of combining
bits in a byte
• These can be ordered as follows:
00000000
00000001
00000010
00000011
00000100
00000101
...
11111110
11111111
REALTIME SYSTEMS – SMD138
3
Integers
• Now let
- 00000000 denote the integer 0
- 00000001 denote the integer 1
- 00000010 denote the integer 2, etc...
• It’s easy to design hardware that
- maps 00000001 and 00000010 onto 00000011
- maps 00000010 and 00000010 onto 00000100
- ...
- i.e., maps 1 and 2 onto 3, 2 and 2 onto 4, ...
• Thus we can implement addition, subtraction, ...
REALTIME SYSTEMS – SMD138
4
Manipulating bits
• In principle, we could define any bit-manipulating
function by just using addition and subtraction
• However, such a scheme will be very bulky for bit
patterns that don’ rea"y represent integers; e.g., bits
that represent status and command flags in some
hardware device registers
• Here we would rather like operations that could
- map 00000010 and 00000011 onto 00000010
(take the logical AND of the bit values)
- map 00001111 onto 11110000
(invert all bit values)
- etc
REALTIME SYSTEMS – SMD138
5
Bit operations
• Luckily, such operations are available in C:
- Bitwise AND (a & b)
- Bitwise OR (a | b)
- Bitwise XOR (a ^ b)
- Bitwise NOT (~ a)
- Shift bits left (a << b)
- Shift bits right (a >> b)
• However: the standard integer representation isn’t
very convenient for this purpose (which bits are
actually set in the representation of 137?)
• Writing bit strings isn’t very convenient either...
6
Alternative bases
• Notice: the decimal number system uses the base 10
(a digit to the left is worth 10 times more)
• The binary number system uses the base 2
(a digit to the left is worth twice as much)
• Idea: use a base that is > 2, but still a power of 2
• That should give shorter constants, but still
provide for good correspondence with bit patterns
• Two alternative bases have become popular:
- The octal system (base 8)
- The hexadecimal system (base 16)
7
The octal system
• Denote three bits by a number 0 - 7:
000 (0)
001 (1)
010 (2)
011 (3)
100 (4)
101 (5)
110 (6)
111 (7)
• A byte is then given by 3 octal numbers, of which
the first is < 4
• In C, all numbers prefixed by 0 are octal
REALTIME SYSTEMS – SMD138
8
The hexadecimal system
• Denote four bits by a “number” 0 - 9, a - f:
0000 (0) 1000 (8)
0001 (1) 1001 (9)
0010 (2) 1010 (a)
0011 (3) 1011 (b)
0100 (4) 1100 (c)
0101 (5) 1101 (d)
0110 (6) 1110 (e)
0111 (7) 1111 (f)
• A byte is given by 2 hexadecimal numbers (nibbles)
• In C, all numbers prefixed by 0x are hexadecimal
9
Examples
10
Bitwise AND
0xc1 & 0xea = 0xc0
0xc1 : 11000001
0xea : 11101010
0xc0 : 11000000
1 AND 0 = 0
0 AND 1 = 0
0 AND 0 = 0
0 AND 1 = 0
0 AND 0 = 0
0 AND 1 = 0
1 AND 1 = 1
1 AND 1 = 1
REALTIME SYSTEMS – SMD138
11
Bitwise OR
0xc1 & 0xea = 0xeb
0xc1 : 11000001
0xea : 11101010
0xeb : 11101011
1 OR 0 = 1
0 OR 1 = 1
0 OR 0 = 0
0 OR 1 = 1
0 OR 0 = 0
0 OR 1 = 1
1 OR 1 = 1
1 OR 1 = 1
REALTIME SYSTEMS – SMD138
12
C.f. logical AND, OR
13
More bitwise operations
• Bitwise XOR
0xc1 ^ 0xea = 0x2b 11000001 (0xc1)
11101010 (0xea)
00101011 (0x2b)
• Bitwise NOT
~ 0xc1 = 0x3e 11000001 (0xc1)
00111110 (0x3e)
14
Accessing a single bit
• Setting a bit
0xc1 | 0x08 = 0xc9 11000001 (0xc1)
00001000 (0x08)
11001001 (0xc9)
• Clearing a bit
0xc1 & 0xbf = 0x81 11000001 (0xc1)
10111111 (0xbf)
10000001 (0x81)
• Alternatively
0xc1 & (~0x40) = 0x81 01000000 (0x40)
10111111 (0xbf)
15
Shifting
• Shifting left
0x1c << 2 = 0x70 00011100 (0x1c)
00000010 (2)
01110000 (0x70)
• Shifting right
0x1c >> 2 = 0x07 00011100 (0x1c)
00000010 (2)
00000111 (0x07)
• “New” bits are zero (or sign-extensions – see later)
• Note that the second argument is really treated as
an integer, not just a bit pattern
16
Using bit numbers
• Setting bit number 3
1 << 3 = 0x08 00000001 (1)
00000010 (3)
00001000 (0x08)
• Setting bit number 0
1 << 0 = 0x01 00000001 (1)
00000000 (0) Bit numbers
00000001 (0x01) are counted
• Setting bit number x from right,
starting from 0
1 << x = ... 00000001 (1)
(x)
...00100... (...)
REALTIME SYSTEMS – SMD138
17
Storage sizes
• A byte corresponds to the type char in C
• A 32-bit word corresponds to an int on most
machines, a long on some (check!)
• A 16-bit word mostly maps to a short
• Chopping off higher bits:
int value = expr;
char abyte;
abyte = value;
• Sign-extending up to higher bits:
char abyte = expr;
int value;
value = abyte;
REALTIME SYSTEMS – SMD138
18
Signed integers
• Swapping the two range halves:
10000000 (-128)
10000001 (-127) char : (-128) – 127
... short : (-32768) – 32767
11111110 (-2)
long : (-2147483648) – 2147483647
11111111 (-1)
00000000 (0)
00000001 (1)
00000010 (2) Note:
... 0xffffffff < 0 = TRUE
01111110 (126)
01111111 (127)
REALTIME SYSTEMS – SMD138
19
Sign extension
• Shifting negative numbers to the right
char num = -128; 10000000 (-128, or 0x80)
num = num >> 2; 00000010 (2)
( num == -32 ) 11100000 (-32, or 0xe0)
• Note: sign-extension only applies to signed types
• Use variables of unsigned type to prevent sign-
extension when shifting right:
unsigned char val = 0xf0; 11110000
val = val >> 4; 00000010
( val == 0x0f ) 00001111
20
Separate bus architecture
21
Memory-mapped architecture
data bus
address bus
22
Accessing I/O ports
23
Memory-mapped I/O
• Addressing a byte
volatile char *ptr = (volatile char *)0xffff0004;
char tmp = *ptr;
*ptr = new_val;
• Addressing a word
volatile int *ptr = (volatile int *)0xffff0004;
char tmp = *ptr;
*ptr = new_val;
• The volatile keyword ensures that the compiler
isn’t fooled into optimizing the pointer access as if
it pointed to real memory
24
Big vs. little endianess
• Where is the “first” byte of a word – to the right or
to the left?
• Example:
long a = 0x12345678;
char *p = (char *)&a;
printf(”%02X\n”, *p);
• What gets printed depends on the architecture
- On a little-endian machine (Intel): 0x12
- On a big-endian machine (most others): 0x78
• Use caution when treating words as bytes
(and vice versa)!
REALTIME SYSTEMS – SMD138
25
Examples
26
Examples
27
Examples
28
Examples
29
Note:
• There is nothing that guarantees that reading and
writing on the same address will refer to the same
register
• For example, some devices map a status register
(for reading) and a command register (for writing)
to the same address:
#define IS_READY (1 << 5)
#define CONVERT (1 << 5)
#define STATUS_REG 0x34c
#define CMD_REG 0x34c
30
Shadowing
• The correct way to update such overlayed registers
is to use a shadow:
#define CONVERT (1 << 5)
#define CMD_REG 0x34c
char cmd_shadow;
...
cmd_shadow |= CONVERT;
out8(CMD_REG, cmd_shadow);
• It’s important to let all changes to CMD_REG be
reflected in cmd_shadow as well
• Notice the convenient assignment sytax C provides
var op= expr; means var = var op expr;
REALTIME SYSTEMS – SMD138
31
Single write
32
Reading multi-bit values
• Masking out a multi-bit value, and then shifting:
#define DONE (1 << 3)
#define SIZE1 (1 << 4)
#define SIZE2 (2 << 4)
#define SIZE3 (3 << 4)
#define SIZEMASK (3 << 4)
#define READY (1 << 6)
...
switch ((in8(0xfff0) & SIZEMASK) >> 4) {
case 1: ...
case 2: ...
case 3: ...
}
REALTIME SYSTEMS – SMD138
33
A slightly bigger example
34