Keywords

These keywords were added by machine and not by the authors. This process is experimental and the keywords may be updated as the learning algorithm improves.

Computers are made up of what can be thought of as tiny switches that are either on or off, and therefore can only process ones and zeros. If you represent on as the number 1 and off as the number 0, you are left with the problem of figuring out a way to calculate using one ones and zeros. Computer scientists solved this problem by doing calculations in binary (base 2) arithmetic, which uses only the digits 0 and 1 to represent any number, just as our familiar base 10 arithmetic uses the digits 0 through 9. Search online for tutorials on “binary arithmetic” to learn more about this—we like one at the Khan Academy ( www.khanacademy.org/math/algebra-home/alg-intro-to-algebra/algebra-alternate-number-bases/v/number-systems-introduction ) and also this one: http://ryanstutorials.net/binary-tutorial/binary-arithmetic.php .

The second problem computer designers needed to solve was how to create a mix of hardware and software to control how data flows through the computer. They did that with logic gates—components that take binary inputs and perform Boolean operations on them. Boolean operations take one or more binary inputs and create one binary output, and logic gates are the physical devices that do these operations (the “tiny switches” we mentioned in the previous paragraph)..In this chapter, we create simple models of logic gates that you can use to learn about the basic components of computer logic.

Logic Gates

A computer chip may be composed of millions of logic gates packaged together. You can represent a circuit of any complexity by just few types of gates, one of which have just one input, the rest of which have two. Some logic gates are combinations of other gates.

Types of Logic Gates

The one-input gate is the NOT gate, which flips its input (a 1 becomes a 0, and a 0 becomes a 1). As you will see, the NOT prefix in general does this flipping function when applied to other gates that follow (such as AND and NAND).

All the other gates have two inputs:

  • The AND gate outputs a 1 if both of its inputs are 1. Otherwise, it outputs a 0.

  • The NAND (short for “NOT AND”) gate negates the output of an AND gate.

  • The OR gate outputs a 1 if either (or both) of its inputs are 1.

  • The NOR (“NOT OR”) gate outputs a 1 only if both inputs are 0 (negates an OR gate).

  • The XOR gate (“exclusive OR,” pronounced “ex-or”) outputs a 1 if exactly one of its inputs is 1.

  • The XNOR (“exclusive NOT OR”) outputs a 1 if both inputs are 0 or both inputs are 1 (negates XOR).

If we collect these inputs and outputs in tabular form, the result is called a truth table (shown in Table 7-1).

Table 7-1. Truth Table for Two-Input Gates

Note

It is possible to create all the other gates out of a combination of all NOR gates or all NAND gates. This was discovered by Charles Sanders Peirce in the 1880s but was not published until 1933, years after he died. For that reason, the NOR gate is sometimes called Peirce’s arrow . There are also other equivalencies that involve using the “NOT” of the gate’s inputs, called De Morgan equivalent s. For example, the AND gate is the same as an OR gate with its outputs and inputs all negated. We experiment with a few of these when we build some logic circuits later in the chapter.

Physical Gate Components

Electrically switched logic components have been around for about 80 years. Claude Shannon worked on an early computing machine using electrical switches for gates at MIT in the late 1930s, and later worked at Bell Labs. The invention of the vacuum tube and then the transistor allowed for gates to be made smaller and smaller as those components shrank.

Currently you can buy computer chips with many logic gates. Field-programmable gated arrays (FPGAs) allow different types of gates to be enabled on a chip by software, for maximum flexibility.

It is difficult to purchase individual gates as a consumer. We found one particular chip that has 8 gates and sells for about 39 cents if you buy one at a time. Creating a demonstrator with a physical chip that correctly handles an input and output for one component is tricky, as we discuss in the next section.

Abstract Representations

The gates have standard representations in logic diagrams. Figure 7-1 shows our representation in the model. In a diagram, they are not normally labeled “AND” and “NOR” and so on—just the outline shape is shown.

Figure 7-1.
figure 1

The individual gate symbols, as we represent them in our models

By convention, inverting gates (NOT, NAND, NOR, and XNOR) have a circle on their output side (the top, as arranged in Figure 7-1). The exclusive OR gates (XOR, XNOR) have a double bar on their input side.

The colors in Figure 7-1 are not significant and were just chosen to make finding pieces easier when you have created a lot of them, without having excessive numbers of spools of filament on the go.

Note

In a real electronic gate, the input would have either a high or low voltage, which would be interpreted as 1 or on, or 0 or off, respectively. Here we have static models with a bar-shaped end to show a 1 and a circle on the end to show a 0. The NOT gate in Figure 7-1, for example, has an input (on the bottom) of 0 and an output (top) of 1.

The Model

Real electronic gates change the state of their inputs and outputs based on electrical signals flowing through them. This means that a realistic model somehow has to mimic electrical energy flowing through wires. We wanted a purely mechanical—and preferably very simple—model instead that would capture the behaviors of gates (and circuits made up of them) while not requiring any actual flowing of electricity or electronic components.

Caution

If you print them at the default size used for this chapter, some of these parts are very small. They should be kept away from young children and not used as toys.

Gates

We wanted to create a very simple model for each gate. The way we did that was to create all the possible combinations of input and output for each gate and then use them like pieces of a jigsaw puzzle to create circuits. Figure 7-2 shows all the possible combinations of input and output for NOR and NAND gates. The other gates (except NOT) have similar sets of four options per gate. NOT just has two—one for each input.

Figure 7-2.
figure 2

The full set of NAND and NOR gates

To put it another way, we made the truth tables visible in plastic parts. As noted earlier, a round input or output should be interpreted as 0 or off, and a bar-shaped one as 1 or on. Although this creates a lot of pieces, it has the virtue of being extremely simple to print and very tactile and visual.

Wires

The next challenge was to figure out how to connect these pieces to each other. We created several different types of connectors, or wires, as we will refer to them from here on. The model creates both the types of wire that have connections for zeros and connections for ones.

Side Connection Wires

The first type of wire is shown in Figure 7-3. The model allows just one input, which can be branched into many outputs. In Figure 7-3, the outputs are on the bottom of the pieces as pictured, and the inputs on the top. Since each wire is connecting outputs that are a 1 only to inputs of another gate that are also 1, and similarly only 0 to 0, the connectors only need to connect circles (zeros) to circles and bars (ones) to bars.

Figure 7-3.
figure 3

The simple connectors (different for 0 and 1 terminations). The model refers to these as side wires.

The wires can have an arbitrary number of outputs, and the outputs can be on both sides of the input. To simplify specifying the number of outputs desired, the number passed to the wires module only specifies the number of connectors on one side of the input.

The sign of the number is used to specify whether there should be a connector on the other side as well, so to include a (single) connector on the other side, a negative number is used. The absolute value of the number of connections is how many connectors are to the left of the input connection in Figure 7-3. So, from top to bottom, these are a –1 wire, a 3 wire, and a 5 wire.

Back Wires and Risers

We realized we needed feedback wires —connections that go from the output of one gate back to the input of another. These are shown in Figure 7-4.

Figure 7-4.
figure 4

The feedback wires, different ones for 0 and 1 terminations, shown here with (top) and without (bottom) risers that allow crossing. The model refers to these as back wires and the risers as risers. For scale, an AND gate is shown with an attached riser.

We also created risers, spacers that can be used to elevate one wire above another in case our wires need to cross each other. Figure 7-4 shows one feedback wire with risers and one without. The risers can be stacked if necessary.

Connecting Wires

Finally, we need to represent just a wire carrying a signal, as well as a way to show an input or an output signal coming into our little systems. We created the pieces in Figure 7-5 for a wire carrying a 0 or 1.

Figure 7-5.
figure 5

Input/output pieces for 0 (top) and 1 (bottom) input or output signals. The model refers to these as forward wires.

Just Drawing on Paper

The other way to connect up a circuit is to use the gates and just draw the connections between them on a piece of paper, using a different color marker for 1 and 0. You can, for example, use blue for 1 and red for 0. We show an example of this when we create an adder circuit later in the chapter.

Printing the Pieces

Listing 7-3 shows the model for all the pieces , both gates and wires. The model is designed so that you can export an STL with a complete set of one gate (all the possible combinations of inputs and outputs) or a specified set of wires.

To print a particular gate, you remove the ! from the piece you do not want to print (gates or wires). OpenSCAD has a convention that something with a ! in front of it is the only routine that should be called. Thus, we remove a ! to disable printing whichever we do not want to create (gates or wires). The changes are detailed in the following list:

  • To print a particular gate:

    • Disable the printing of wires by removing the ! from !wires(side,5).

    • Change !gates(none) to !gates( name of the gate ), for example !gates(or).

  • To print a particular type of wire (see captions for Figures 7-3 through 7-5 for options):

    • Disable the printing of gates by removing the ! from !gates(none).

    • Change the !wires(side, 5) to !wires( name of the type of wire, parameter ), for example !wires(side, -5).

If you are printing forward or back wires, the parameter does not do anything. It is only used for the side wires.

Note

If your wires and gates have trouble fitting into each other, you can adjust the clearance variable up a little or increase the size variable (which will make everything bigger).

Listing 7-1. Circuit Model

// Model of logic gates and connectors // File gates.scad // Rich "Whosawhatsis" Cameron, March 2017 // Create logic gates with all permutations of inputs and outputs // A "1" or "TRUE" is a crossbar // A "0" or "FALSE" is circle // And connectors, input, and output pieces size = 30; //Scaling in mm - roughly bounding box of gate symbols thick = 1; //Line thickness; connector lines are twice this height = 3; //Max height above platform of gates, mm fontsize = size / 5; fontweight = thick; clearance = .4; // Parameter governing clearance of parts                 // that fit into each other // Remove the "!" from the piece you do NOT want to print // (gates or wires) // To make a set of gates, // Replace "none" with one of the names of gates // listed later in the model. // All possible permutations of inputs and outputs // for that gate are printed. The optional second parameter // "rows" determines how many rows these will be split // into on the printer platform. !gates(none); // Or, for wires, replace the first parameter of "wires" // with one of the types of wires named later in the model // to print a set of those wires. // The second parameter is the number of connection points. // Negative numbers have connections on two sides !wires(side, -5); //gates none = 0; or = 1; xor = 2; and = 3; not = 4; nor = 5; xnor = 6; nand = 7; //wires side = 0; //connectors branching sideways forward = 1; // data input, with a 1 or 0 back = 2; //feedback wires riser = 3; //offsets two layers $fs = .2; $fa = 2; // gates makes multiple instances of objects define by gate // second parameter is how many are in a row module gates(type, row = 2) {    if(type == not) for(i = [0:1]) translate([       (size + 2) * (i % row),       (size + 15) * floor(i / row),       0    ]) gate(type, [i]);    else if(type == none) wire(forward);    else for(i = [0:3]) translate([       (size + 2) * (i % row),       (size + 14) * floor(i / row),       0    ]) gate(type, [floor(i / 2), i % 2]); } // Module gate makes the gates module gate(type, in = [0, 0]) for(h = [0, height - 1]) {    linear_extrude(height = h + 1, convexity = 5) {       if(type % 4 == or) {          _or(in, h ? thick : 0);          _out(             xor(in[0] || in[1],             type >= not),             h ? thick : 0,             (type >= not) ? true : false          );          translate([0, -size * .15, 0])             offset(fontweight/2 - fontsize * .075) text(                (type >= not) ? "NOR" : "OR", size = fontsize,                halign = "center",                valign = "center",                font = ":style=Bold"              );       }       else if(type % 4 == xor) {          _xor(in, h ? thick : 0);          _out(             xor(xor(in[0], in[1]), type >= not),             h ? thick : 0,             (type >= not) ? true : false          );          translate([0, -size * .08, 0])              offset(fontweight/2 - fontsize * .075) text(                 (type >= not) ? "XNOR" : "XOR",                 size = fontsize,                 halign = "center",                 valign = "center",                 font = ":style=Bold"              );       }       else if(type % 4 == and) {          _and(in, h ? thick : 0);          _out(             xor(in[0] && in[1], type >= not),             h ? thick : 0,             (type >= not) ? true : false          );          translate([0, -size * .15, 0])             offset(fontweight/2 - fontsize * .075) text(                (type >= not) ? "NAND" : "AND",                size = fontsize,                halign = "center",                valign = "center",                font = ":style=Bold"             );       }       else if(type % 4 == none) {          if(type == not) {             _none(in, h ? thick : 0);             _out(                xor(in[0], type >= not),                h ? thick : 0,                (type >= not) ? true : false             );             translate([0, -size * .25, 0])                offset(fontweight/2 - fontsize * .075) text(                   (type >= not) ? "NOT" : "",                   size = fontsize,                   halign = "center",                   valign = "center",                   font = ":style=Bold"                );          } else _forwardwire([in[0]], h ? thick : 0);       }    } } module wires(type, value = 1) {    if(type == side) wire(type, [(value < 1) ? 1 : 0, abs(value)]);    else wire(type); } module wire(type, w = [0, 1]) {    if(type == side) {       linear_extrude(height = height, convexity = 5) for(i = [0, 1])          translate(             i * [-size * 2 / 3 * (w[1]) - thick,             -thick * 4,             0          ]) rotate(i * 180) _crosswire([i], thick, w = w);    } else if(type == forward) {       linear_extrude(height = height, convexity = 5) for(i = [0, 1])          translate(i * [-size / 3 - thick * 7, 0, 0]) rotate(i * 180)             _forwardwire([i], thick);    } else if(type == back) {       linear_extrude(height = height, convexity = 5) for(i = [0, 1])          translate(i * [-thick * 4, 10 + thick + 5, 0])             _backwire([i], thick);    } else if(type == riser) {       for(i = [0, 1]) translate(i * [0, 0, 0]) rotate(i * 180)          _wireriser([i], thick, height);    } } // OpenSCAD doesn't have a built-in xor operator, so we need a // function. function xor(a, b) = (a || b) && !(a && b); module _or(in = [0, 0], width = 0, l = 10) difference() {    union() {       if(l) _in(in, width, l = l);       difference() {          intersection_for(i = [-1, 1])             translate([i * size / 2, -size * .366, 0]) circle(size);          translate([0, -size - size * .366, 0]) circle(size);       }    }    if(width) offset(-width) _or(l = 0); } module _xor(in = [0, 0], width = 0, l = 10) difference() {    union() {       if(l) _in(in, width, l = l);       _or(l = 0);    }    if(width) union() {       offset(-width) difference() {          _or(l = 0);          translate([0, -size - size * .366, 0])             circle(size + width * 3);       }       translate([0, -size - size * .366, 0]) difference() {          circle(size + width * 3);          circle(size + width);       }    } } module _and(in = [0, 0], width = 0, l = 10) difference() {    union() {       if(l) _in(in, width, l = l);       hull() {          circle(size / 2);          translate([-size / 2, -size / 2, 0])             square([size, size / 4]);       }    }    if(width) offset(-width) _and(l = 0); } // generic gate symbol (used for NOT) module _none(in = [0], width = 0, l = 10) difference() {    union() {       if(l) _in(in, width, l = l);       hull() {          translate([0, size * .45, 0]) circle(size * .05);          translate([-size * .45, -size * .45, 0]) circle(size * .05);          translate([size * .45, -size * .45, 0]) circle(size * .05);       }    }    if(width) offset(-width) _none(l = 0); } // Create connectors module _forwardwire(in = [0], width = 0, l = 10) {    for(i = [0:len(in) - 1]) translate([       (len(in) > 1) ? size * 2 / 6 / (len(in) - 1) * i - size / 6 : 0,       0,       0    ]) {       _in(in, width, l = l);       _out(in[0], width, l = l);       square([width * 2, size], center = true);    } } module _backwire(in = [0], width = 0, l = 10) {    for(i = [0:len(in) - 1]) translate([       (len(in) > 1) ? size * 2 / 6 / (len(in) - 1) * i - size / 6 : 0,       0,       0    ]) {       for(i = [0, 1]) rotate(180 * i) {          translate([             -size / 2,             -size - l * 3,             0          ]) _out(in[0], width, l = l);          translate([-width, size / 2 + l * 3, 0])             square([size / 2 + width * 2, width * 2]);       }       square([width * 2, size + l * 6], center = true);    } } module _forwardwire(in = [0], width = 0, l = 10) {    for(i = [0:len(in) - 1]) translate([       (len(in) > 1) ? size * 2 / 6 / (len(in) - 1) * i - size / 6 : 0,       0,       0    ]) {       _in(in, width, l = l);       _out(in[0], width, l = l);       square([width * 2, size], center = true);    } } module _crosswire(in = [0], width = 0, l = 10, w = [0, 0]) {    for(side = [0, 1]) mirror([side, 0, 0]) if(w[side]) difference() {       union() {          translate([-width, -width, 0])             square([                size * 2 / 3 * (w[side] - .5) + width * 2,                width * 2             ]);          for(i = [1:w[side]]) translate([             size * 2 / 3 * (i - .5),             0,             0          ]) {             translate([0, l / 4, 0])                square([width * 2, l / 2], center = true);             translate([0, l / 2, 0]) offset(width * 2 + clearance)                _end(in[0], width);          }          translate([0, -l / 4, 0])             square([width * 2, l / 2], center = true);          translate([0, -l / 2, 0]) offset(width * 2 + clearance)             _end(in[0], width);       }       offset(clearance) {          for(i = [1:w[side]]) translate([             size * 2 / 3 * (i - .5),             0,             0          ]) {             translate([0, l / 2 + width * 2, 0])                square([width * 2, width * 4], center = true);             translate([0, l / 2, 0]) _end(in[0], width);          }          translate([0, -l / 2 - width * 2, 0])             square([width * 2, width * 4], center = true);          translate([0, -l / 2, 0]) _end(in[0], width);       }    } } module _wireriser(in = [0], width = 0, h = height) {    translate([0, -10, 0]) difference() {       union() {          linear_extrude(h + 2) offset(width * 2)             _out(in[0], width, l = 0);          linear_extrude(h * 2 + 2, convexity = 5) intersection() {             offset(width * 2) _out(in[0], width, l = 0);             translate([0, -width * 5, 0])                _out(in[0], width, l = width * 5);          }       }       translate([0, -width * 5, 0])          linear_extrude(h * 2 + 2, center = true, convexity = 5)             offset(clearance) _out(in[0], width, l = width * 5);    } } module _in(in = [0], width = 0, l = 10) for(i = [0:len(in) - 1]) {    translate([size * 2 / 3 * i - size / 3, 0, 0]) {       if(len(in) > 1) translate([0, -size * .4 - l / 2, 0])          square([width * 2, l + size * .2], center = true);       else {          translate([-width, -size / 2 - l / 2 - width, 0])             square([size / 3 + width * 2, width * 2]);          translate([size / 3, -size * .4, 0])             square([width * 2, l + size * .2], center = true);          translate([             0,             -size * .4 - l / 2 - (l / 2 + size * .2) / 2,             0          ]) square([width * 2, l / 2], center = true);       }       translate([0, -size / 2 - l, 0]) {          _end(in[i], width);       }    } } module _out(out = 0, width = 0, inverting = false, l = 10) {    difference() {       union() {          translate([0, size / 2 + l / 2 - .5, 0])             square([width * 2, l + .5], center = true);          translate([0, size / 2 + l, 0]) _end(out, width);          if(inverting) translate([             0,             size * .5 - thick + thick * 2.5,             0          ]) circle(thick * 2.5);       }       if(width) offset(-width) _out(inverting = inverting, l = 0);    } } module _end(on = true, width = 0) {    if(on) square([width * 6, width * 2], center = true);    else circle(width * 2); } // end model

Making Model Circuits

Now that we have all the pieces, we can make some model circuits. Just to start, you may find it useful to use the full set of each type of gate (like the ones in Figure 7-2) as sort of plastic flash cards to remind yourself of the truth table of that gate. Then you can begin exploring combinations of the gates.

Gates as Combinations of Others

Gates, as mentioned earlier, can be constructed as combinations of other gates. This is a good way to get more fluency and intuition about the logical relationships among the gates. Some of these are called De Morgan equivalents after the 19th century British mathematician who first wrote them down.

As noted earlier, Peirce showed that NOR gates alone or NAND gates alone can be used to create any of the others ( https://en.wikipedia.org/wiki/Logic_gate ).

We create two equivalents here: AND, made up of NANDs (Figure 7-6 and Table 7-2), and OR, made up of NANDs (Figure 7-7 and Table 7-2). We can check that these work for all possible combinations with our models, or with a truth table. We show a combination here of one case with the models and the full truth table.

Figure 7-6.
figure 6

AND gate made up of NANDs (inputs on bottom, output on top)

Table 7-2. Truth Table for Figure 7-6 (AND Made Up of NANDs)
Figure 7-7.
figure 7

OR gate made up of NANDs (inputs on bottom, output on top)

Table 7-3. Truth Table for Figure 7-7 (OR Made Up of NANDs)

Flip-flop

One of the most basic logical components of a computer is the flip-flop, sometimes called an S-R latch (for set-reset) or a bistable multivibrator . Whatever we choose to call it, it is a way to store a single 1 or 0 (single bit) of information. You can read more about it at https://en.wikipedia.org/wiki/Flip-flop_(electronics) .

The flip-flop shown in Figure 7-8 takes two inputs and, by feeding the outputs back into the opposite gate input, keeps its two outputs in opposite states from each other. Figure 7-8 is called an SR NOR latch, because it is made up of two NOR gates with crossed feedback.

Figure 7-8.
figure 8

Flip-flop with NOR gates (inputs on bottom, outputs on top)

Figure 7-9 is a closeup of the crossed feedback paths, using a riser on each end of one of the feedback paths to keep the crossed paths separated with one passing over the other. Try building this circuit with several combinations of inputs. Note that having a pair of inputs that are both 1 is impossible to build in a static consistent way. In the flip-flop’s stable states, the two inputs cannot both be 1. The outputs are always the opposite of each other.

Figure 7-9.
figure 9

Flip-flop showing crossover of feedback

There are other variations of the flip-flop. Figure 7-10 uses NAND gates to accomplish the same thing, called an SR-bar NAND latch. In this latch, the state with both inputs equal to 0 is not allowed.

Figure 7-10.
figure 10

Flip-flop with NAND gates (SR-bar NAND)

Try building these gates with the various possible outputs and see what happens to the two outputs (and when you start to “race” unstably back and forth on the feedback paths).

Adder

Another fundamental circuit in a computer is an adder. An adder is used to add two binary numbers—either two zeros, two ones, or one of each. When we add in binary, anything over 1 has to “carry” to the next digit, so adding requires keeping track of inbound carried values and outbound ones. Adders are typically cascaded (used in long sets with one feeding the next) to allow addition of many-bit numbers. Read more at https://en.wikipedia.org/wiki/Adder_(electronics) .

We have built a one-bit adder out of AND, OR, and XOR gates in Figure 7-11 and we have drawn the connections (with red lines being 0 and blue lines, 1) in Figure 7-12. It might be easier to see the connections in Figure 7-12 because some of the inputs have to cross others in Figure 7-11. Here is what is going on in both figures:

Figure 7-11.
figure 11

Adder circuit with connectors

Figure 7-12.
figure 12

Adder circuit, hand drawn

  • We are adding the two values shown by the orangey-red and blue dots (at the bottom of the figure). Both of those are a 1, so the output should be 10 in binary (otherwise known as 2 in our base-10 system).

  • The output value is noted by the yellow dot at the upper left. It is a zero, which is correct—when you add 1 + 1 in binary, you get a 0 plus a 1 to carry to the next place.

  • The carry values are shown by green dots. The lower one is the value carried in (0 in this case), and the one at the top right is the value carried out to the next stage (1 in this case, because we are adding 1 and 1, which gives us a 1 to carry).

  • For the physical system in Figure 7-11, we put white dots under the two places where the lines cross, because it is difficult to see in a 2D picture.

  • We used a feedback connector to link the upper AND to the XOR in the upper right because the geometry worked out better that way, even though strictly speaking this is not a feedback loop.

In short, we have succeeded in showing that, in binary, 1 + 1 equals 0 plus carry a 1 to the next digit, or 1 + 1 = 10. Try tracing through this case to see how the logic plays out for different inputs.

The hand-drawn circuit is probably the way to go for anything this complex or beyond. We find the plastic pieces add value in keeping track of what you want the values at various points to be, but just drawing the circuits reduces the time required to make something. We found it helped to create a dot of the correct color where an input was going to connect and then draw the lines to those dots.

Caution

We have found that as we build these logical structures it is easy to get engrossed with the physical part of the modeling. You may want to “look ahead” a little on paper so you do not get surprised by logical impossibilities (for instance, an output equal to 1 trying to connect to an input expecting a 0).

Logic diagrams lend themselves to a somewhat different topology than the convention of all inputs lined up on one side and all outputs on the right. Rather, you might want to think about what gates are in parallel with each other.

Thinking About These Models: Learning Like a Maker

When we started working on these models, we thought we would be able to come up with cute mechanical equivalents of each gate, which might operate switches or rubber bands or some other simple part to “flow” a signal through a simulated circuit. We noted with amazement the complex mechanical logic gates we found online, which were difficult to understand and to validate. Then we discovered that the issue was that in some cases one had to store energy or a previous state in the circuit, in addition to the gates. For example, if a NOT gate gets a 0 as input, it has a 1 coming out. Thus, flow of whatever the analog of electricity is has to come from some reservoir somewhere. Mechanical gates might have been hard to print, too, and we try to make all our models as easy to print as possible.

We decided we would sacrifice complex inner workings for more somewhat duplicative parts and use a different part for different pairs of inputs for each gate. The next issue was the connectors—there needed to be connectors that could be used to lay out circuits with a variety of geometries, including the issue that each gate has an output in the middle that feeds into an input in the next gate that is offset from the center. In the end, we came up with the current connector set as a compromise that was not too complex but that would allow for some limited but interesting explorations, besides being flash cards of a sort.

Where to Learn More

A next step might be to try to create circuits with actual electronic components. Sparkfun has developed LogicBlocks Kits with individual gate components, described at https://learn.sparkfun.com/tutorials/logicblocks--digital-logic-introduction . These actually show the ones and zeros, with LEDs on each “gate” that are lit or not as appropriate.

You can also buy computer chips with several gates per chip, which expect to get an input signal on defined pins and will output the result on another defined pin. You may need to do some soldering for this option.

It is quite possible to simulate gates with fairly simple code, too. There are versions programmed in the Scratch visual programming environment, like this one: https://scratch.mit.edu/projects/66610/ .

Teaching with These Models

Teaching circuit logic with actual components is always the ideal. However, these components might not be available if you are teaching Boolean logic in a software or math class, or if your budget is limited. You can use the models in this chapter as the basis of a very simple simulation of circuits with logic gates.

If you search the NGSS standards for a reference to circuits, the system responds with a note that the focus of the standards is on the idea of energy transfer, not types of circuits ( www.nextgenscience.org/search-standards?keys=circuits ), so we were not able to find any explicit guidance there. They note that circuits could be used in conjunction with teaching the Energy standards. These might be more appropriate to a math class or a coding class.

Project Ideas

The most obvious thing to do with this is to play with simulating circuits or, for that matter, the logic in various coding algorithms. You might try to lay out some classic simple computing algorithms. Most immediately, you can print out enough components to make two or three cascading steps for a multi-bit adder.

You might also consider how to use these pieces to teach simple logical constructs, and perhaps how to model and connect more-complex parts with multiple inputs and outputs. Perhaps there could be a group “Rube Goldberg Logic” exercise that starts with given inputs and branches out through gates (consistently!) to some planned end.

Summary

This chapter discusses what logic gates are and explores their role in binary computing. We create a set of simplified printable models of logic gates and “wires” of various sorts to connect them together. Then we use these components to create one gate in the form of combinations of other gates, along the way dealing with the problem of modeling wires that are crossing each other. Finally, we develop models of flip-flops and adders to create physical intuition of some of the foundational logical constructs of modern digital computing.