Topics for today:
The truth table looks like this:
a b a==b 0 0 1 0 1 0 1 0 0 1 1 1What does the Karnaugh map for this function look like?
What is a minimal sum-of-products (SOP) for this function?
What would a combinational circuit for this function look like?
a1 a0 b1 b0 a==b 0 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 0 1 1 0 0 1 0 0 0 0 1 0 1 1 0 1 1 0 0 0 1 1 1 0 1 0 0 0 0 1 0 0 1 0 1 0 1 0 1 1 0 1 1 0 1 1 0 0 0 1 1 0 1 0 1 1 1 0 0 1 1 1 1 1How might this function be useful in a computer? It can be used to implement the == operator in C++.
What does the Karnaugh map for this function look like?
What is a minimal SOP for this function?
One way to implement this function is to build a circuit based on the minimal SOP. But what if n=3? Then there are 6 variables. The K-map would have 64 0s and 1s. If n=4, then there are eight variables. The K-map would have 256 0s and 1s. On modern machines, this circuit would have to work for n=32. That would yield a K-map with 18,446,744,073,709,551,616 0s and 1s, and keeping track of the adjacencies would be difficult on two-dimensional paper.
Another way to implement this function is to link together many 1-bit equality circuits. How would you do that?
The truth table looks like this:
s a b MUX(a,b,s) 0 0 0 0 0 0 1 1 0 1 0 0 0 1 1 1 1 0 0 0 1 0 1 0 1 1 0 1 1 1 1 1How might this function be useful in a computer? Think of it as a logic-level version of the if/else statement. It basically says if (s) output = a; else output = b;
For n=2, the truth table looks like this:
s a1 a0 b1 b0 c0 c1 0 0 0 0 0 0 0 0 0 0 0 1 0 1 0 0 0 1 0 1 0 0 0 0 1 1 1 1 0 0 1 0 0 0 0 0 0 1 0 1 0 1 0 0 1 1 0 1 0 0 0 1 1 1 1 1 0 1 0 0 0 0 0 0 1 0 0 1 0 1 0 1 0 1 0 1 0 0 1 0 1 1 1 1 0 1 1 0 0 0 0 0 1 1 0 1 0 1 0 1 1 1 0 1 0 0 1 1 1 1 1 1 1 0 0 0 0 0 0 1 0 0 0 1 0 0 1 0 0 1 0 0 0 1 0 0 1 1 0 0 1 0 1 0 0 0 1 1 0 1 0 1 0 1 1 0 1 1 0 0 1 1 0 1 1 1 0 1 1 1 0 0 0 1 0 1 1 0 0 1 1 0 1 1 0 1 0 1 0 1 1 0 1 1 1 0 1 1 1 0 0 1 1 1 1 1 0 1 1 1 1 1 1 1 0 1 1 1 1 1 1 1 1 1The general n-bit multiplexor function would be used in a computer for multiplexing words (e.g. integers) rather than just bits.
Since this function has multiple outputs, we don't know yet how to make a K-map for it. One way would be to draw two 5-variable K-maps, with the first representing c0 and the second representing c1. This drawing would have 64 0s and 1s.
An easier way to make a circuit out of this mess would be to combine two 1-bit multiplexors.
s0 s1 i0 i1 i2 i3 output | s0 s1 i0 i1 i2 i3 output 0 0 0 0 0 0 0 | 1 0 0 0 0 0 0 0 0 0 0 0 1 0 | 1 0 0 0 0 1 0 0 0 0 0 1 0 0 | 1 0 0 0 1 0 1 0 0 0 0 1 1 0 | 1 0 0 0 1 1 1 0 0 0 1 0 0 0 | 1 0 0 1 0 0 0 0 0 0 1 0 1 0 | 1 0 0 1 0 1 0 0 0 0 1 1 0 0 | 1 0 0 1 1 0 1 0 0 0 1 1 1 0 | 1 0 0 1 1 1 1 0 0 1 0 0 0 1 | 1 0 1 0 0 0 0 0 0 1 0 0 1 1 | 1 0 1 0 0 1 0 0 0 1 0 1 0 1 | 1 0 1 0 1 0 1 0 0 1 0 1 1 1 | 1 0 1 0 1 1 1 0 0 1 1 0 0 1 | 1 0 1 1 0 0 0 0 0 1 1 0 1 1 | 1 0 1 1 0 1 0 0 0 1 1 1 0 1 | 1 0 1 1 1 0 1 0 0 1 1 1 1 1 | 1 0 1 1 1 1 1 0 1 0 0 0 0 0 | 1 1 0 0 0 0 0 0 1 0 0 0 1 0 | 1 1 0 0 0 1 1 0 1 0 0 1 0 0 | 1 1 0 0 1 0 0 0 1 0 0 1 1 0 | 1 1 0 0 1 1 1 0 1 0 1 0 0 1 | 1 1 0 1 0 0 0 0 1 0 1 0 1 1 | 1 1 0 1 0 1 1 0 1 0 1 1 0 1 | 1 1 0 1 1 0 0 0 1 0 1 1 1 1 | 1 1 0 1 1 1 1 0 1 1 0 0 0 0 | 1 1 1 0 0 0 0 0 1 1 0 0 1 0 | 1 1 1 0 0 1 1 0 1 1 0 1 0 0 | 1 1 1 0 1 0 0 0 1 1 0 1 1 0 | 1 1 1 0 1 1 1 0 1 1 1 0 0 1 | 1 1 1 1 0 0 0 0 1 1 1 0 1 1 | 1 1 1 1 0 1 1 0 1 1 1 1 0 1 | 1 1 1 1 1 0 0 0 1 1 1 1 1 1 | 1 1 1 1 1 1 1That is truly a horrible truth table, but sometimes we have to solve truth tables like that, so let's see what a 6-variable K-map would look like:
Hey, that's not so bad when you look at it as a K-map.
Why would you want such a circuit? This is the basis of how array indexing is implemented in a high-level programming language like C++. You provide a k-bit array index and get one of 2k items from memory. Something has to do that conversion from the index to the location of the items, and in a computer that something is a multiplexor.
When both R and S are set to 1, the output of the circuit is a single stored bit that can be thought of as constantly looping through the circuit.
To store a one, we bring R down to 0.
To store a zero, we keep R at 1 and bring S down to 0.
We can build arrays of latches to hold words (e.g. integers). An array of latches is called a register. Special registers in the CPU are made visible to the architecture, i.e., machine language instructions can access special CPU registers.
A 6-transistor SRAM cell looks like this:
The two inverters (i.e., the four middle transistors) provide storage for a single bit. Think of the bit as continously going around and around the two inverters in a counter-clockwise manner. The bit is the output of the top inverter, and the complement of the bit is the output of the bottom inverter as well as the input to the top inverter. The following occurs when we want to read the bit stored in this SRAM cell:
#include <stdio.h> #include <iostream> void int2bin (int, bool[], int); int bin2int (bool[], int); void ripple_carry_add (bool[], bool[], bool[], int); bool parity (bool, bool, bool); bool majority (bool, bool, bool); void subtract (bool[], bool[], bool[], int n); int main (void) { int a = 1234; int b = 5678; int c; bool A[32], B[32], C[32]; int2bin (a, A, 32); int2bin (b, B, 32); ripple_carry_add (A, B, C, 32); c = bin2int (C, 33); std::cout << c << "\n"; subtract (A, B, C, 32); c = bin2int (C, 33); std::cout << c << "\n"; } // convert integer x to n-bit Boolean array v void int2bin (int x, bool v[], int n) { for (int i=0; i<n; i++) { v[i] = (x & 1) == 1; x /= 2; } } // convert n-bit Boolean array v to integer and return it int bin2int (bool v[], int n) { int x = 0; for (int i=n-1; i>=0; i--) { x *= 2; if (v[i]) x++; } return x; } // do a ripple-carry add on n-bit Boolean arrays A and B, storing the result // in n+1-bit Boolean array C void ripple_carry_add (bool A[], bool B[], bool C[], int n) { bool carry; // no carry in; 2-bit parity is XOR C[0] = A[0] ^ B[0]; // two-bit carry is AND carry = A[0] && B[0]; // add corresponding bits of the array, along // with the carry generated initially or from previous // iterations of the loop for (int i=1; i<n; i++) { C[i] = parity (A[i], B[i], carry); carry = majority (A[i], B[i], carry); } // n'th bit is the carry out of the loop C[n] = carry; } // return the parity of x, y, and z bool parity (bool x, bool y, bool z) { return x ^ y ^ z; } // return the majority of x, y, and z bool majority (bool x, bool y, bool z) { return (x && y) || (x && z) || (y && z); } // subtract n-bit Boolean array B from A, placing result in n+1-bit array C // i.e. C = A - B void subtract (bool A[], bool B[], bool C[], int n) { bool complementB[n]; bool negativeB[n+1]; bool one[n]; int i; // we'll subtract B from A by adding -B to A // first we have to find -B, which is the bitwise complement // of B plus 1 // find the logical complement of B for (i=0; i<n; i++) complementB[i] = ! B[i]; // make a 1 one[0] = true; for (i=1; i<n; i++) one[i] = false; // find the negation of B ripple_carry_add (complementB, one, negativeB, n); // find A + (- B) ripple_carry_add (A, negativeB, C, n); }