N2T Unit One: Boolean functions and gate logic
I have recently started Nand to Tetris. This course teaches the foundations of hardware and computer achitecture and is based on the textbook The Elements of Computing Systems by Noam Nisan and Shimon Schocken. As the course proceeds you build a functioning general-purpose computer that is eventually capable of running Tetris and any number of other programs. The hardware is built primarily through simulation software running on Hack, a simplified hardware description language similar to Verilog and VHDL.
In this post I outline what I learned in the first unit. This broadly follows the curriculum but adds extra details I have acquired elsewhere to give the fullest account.
Bits and functions
The workings of a classical computer can be reduced to a series of operations on the binary digits (bits) 0 and 1. A computational process can be represented as a function: data (a series of bits) enters the function in one state and exits in another state. This new state is a product of the function.

The most primitive bit operations are equivalent to the truth-conditions of the logical connectives of Boolean algebra. In logic, the Boolean values are true and false but we will use 1 and 0, to represent these states as bits. There are multiple logical connectives but we will mostly focus on AND, OR, and NOT for simplicity.
The logic of each Boolean connective can be expressed as a function.
NOT can be represented as follows;
$$ f(x) = \lnot (x) $$
NOT (¬) is a unary operator which means it takes one operand (x). We pass a single bit as the operand and the function inverts its value. We can utilise logical truth tables to represent all possible inputs and outputs for the operator:
| x | f(x) = NOT(x) |
|---|---|
| 1 | 0 |
| 0 | 1 |
If NOT receives 0 as an input it will return 1 as the output. If it receives 1 as the input it will return 0 as the output.
AND ($\land$) and OR($\lor$) are binary operators: they receive two operands as input. Therefore we pass two bits to the function. As a bit can be one of two values (0 or 1), there are four possible inputs.
For AND this gives us:
| x | y | f(x) = x AND y |
|---|---|---|
| 1 | 1 | 1 |
| 0 | 1 | 0 |
| 1 | 0 | 0 |
| 0 | 0 | 0 |
AND returns 1 if both input bits are 1, otherwise it returns 0.
OR returns 1 if one or both bits are 1, otherwise it returns 0:
| x | y | f(x) = x OR y |
|---|---|---|
| 1 | 1 | 1 |
| 0 | 1 | 1 |
| 1 | 0 | 1 |
| 0 | 0 | 0 |
Logic gates
The logical function of each of the Boolean operators is implemented at the level of computer hardware by logic gates. Each gate is represented with one or more input pins and a single output pin through which the bits enter and exit. The pins feed into and out of a chip which executes the function.
The diagram below shows the logic gates for AND, OR, and NOT.

Logic gates are an abstraction. In reality, you cannot see a logic gate in a computer. If we were to look inside a chip that implements gate logic we would see an arrangement of transistors, capacitors and resistors connected by wires. These components are configured to mimic the behaviour of the logical operators.
Nand to Tetris starts at the level of gates and does not discuss how the individual gates are realised in electronic circuits. However, I think it is useful to understand the basics of the electrical engineering so we might begin to grasp how it is possible to go from an inert block of metal and silicon to fully functioning computer.
The electronic implementation of gate logic
We can start with the concept of a switch. Consider what is happening when we turn on a light using a wall switch.
When the switch is off, the electrical circuit that connects the bulb to the voltage source is broken. As a result, there is no potential difference between the terminals and the current cannot reach the bulb. When the switch is on, the circuit is complete and the current flows freely to the bulb.
This is represented by the following simple schematic:

This circuit embodies the logic of a NOT gate. Think of the light as 1 and the absence of light as 0. When the switch is on, 1 is the output and 0 is inverted. When the switch is off, 0 is the output and 1 is inverted.
This scenario can be developed to represent the logical behaviour of an AND gate. Imagine now that this room is a bit strange and there are two switches controlling the operation of the bulb. If both switches are off the bulb will not emit light. If one of the switches is on and the other is off the bulb will not emit light. The bulb will only emit light when both switches are on.
The schematic for this scenario embodies the logic of the Boolean AND connective:

Switch-controlled circuits are functionally equivalent to what actually happens inside a computer when logical conditions are implemented via gates. Electrical charge is directed along different routes depending on the value of an on/off condition. However, in modern computers the actual component that controls the flow of current is not a switch.
Whilst we could construct primitive computers with switches, the average CPU has more than a million logic gates. Controlling this number of gates with mechanical switches would be practically impossible and even if it were achieved, it would result in extremely slow processing times.
This is an important point because the computational power of logic gates emerges from their behaviour at scale. Collections of gates are combined to express complex logical conditions that are a function of their individual parts. For this to be possible, the output of a collection of gates needs to be able to be fed into another collection and this it would be difficult to achieve in a mechancial switch-based system.
Instead of mechanical switches, computers use transistors. Transistors are semi-conductors: components that possess an electrical conductivity between that of a conductor and an insulator. This property means they can both impede and expede the flow of electrical charge.
There are different types of transistors but we will focus on basic Bipolar Junction Transistors:

Applying a small amount of current at the base terminal (B) of a BJT allows a larger current to flow from the collector © to the emitter (E). Removing current at the base terminal reduces the flow from collector to emitter. This is because the the emitter and collector are composed of a semi-conductor that has a surplus of electrons (negatively charged) whereas the base has a deficiency of electrons (positively charged). This state creates a modifiable potential difference, reducing or increasing the current based on the voltage.
The base terminal of a transistor is another way of implementating the gate-like behaviour we previously achieved with mechanical switches. However, there is an important difference. With a switch, the circuit is actually broken when it is in the “off” state and there is no current flowing at all. With a transistor, the current drops in the “off” state but a voltage remains.
Because a continuous circuit is an analogue system, the quantities of resistance, voltage and current are not discrete values, they will vary over a given range. Thus “off” corresponds to “low” voltage and “on” corresponds to “high” voltage. The specific stipulation will depend on the circuit design but it is typically the case that a state of 1 or “on” is within the range 2-5V whereas a state of 0 or “off” is within the range 0.0 - 0.8V.
Boolean function synthesis
To recap, elementary computational processes can be represented as logical functions. A function consists in one or more Boolean operators processing bits. For each Boolean operator we can construct a chip that represents its truth conditions. We call these chips logic gates. Logic gates are built with transistors that either block or permit the flow of electric current. This makes them behave in a manner almost identical to mechanical switches.
Now that we know how the individual logic gates work and how they are implemented electronically, we will explore how they can be applied in combination to represent complex logical states that more closely resemble actual computer programs. This process is known as Boolean function synthesis.
We will construct a logic circuit that represents the truth conditions for the following state of affairs:
The team plays on either Monday or Thursday and not at weekends
Let’s call this P for ease of reference.
This complex comprises several simpler atomic expressions:
- The team plays on Monday
- The team plays on Thursday
- The team plays at weekends
The first step is to construct a truth table. On the left-hand side we list all the possible truth values for each individual expression. On the right-hand side, we assign an overall truth value for their combination, based on whether or not they reflect the truth conditions for P.
| x | y | z | P |
|---|---|---|---|
| 1 | 1 | 1 | 0 |
| 1 | 1 | 0 | 1 |
| 1 | 0 | 1 | 0 |
| 1 | 0 | 0 | 1 |
| 0 | 1 | 1 | 0 |
| 0 | 1 | 0 | 1 |
| 0 | 0 | 1 | 0 |
| 0 | 0 | 0 | 0 |
We are only interested in the cases where P is true, so we can discount any lines that result in a truth value of 0 for the complex expresssion. This leaves us with:
| x | y | z | P |
|---|---|---|---|
| 1 | 1 | 0 | 1 |
| 1 | 0 | 0 | 1 |
| 0 | 1 | 0 | 1 |
Parsing each line, the truth table tells us that our complex expression (P) is true in the following scenarios:
- If the team plays on both Mondays and Thursdays but not at weekends
- If the team plays on Mondays but not on Thursdays and not at weekends
- If the team plays on Thursdays but not on Mondays and not at weekends
We can formalise each case:
- (x AND y) AND NOT z
- (x AND NOT y) AND NOT z
- (NOT x and y) AND NOT z
We now have three logical expressions that if constructed with logic gates would result in a partial representation of P. The representation would be partial because each individual expression only conveys a single aspect of the truth of P, not its totality. For example, if we constructed a circuit that represents (x AND NOT y) AND NOT z, this would only cover occasions where the team plays on Mondays but not on Thursdays (or the weekend). It wouldn’t cover the case where the team plays on Thursdays but not Mondays (or the weekend).
We want a circuit that captures all possible instances where P returns true. There are practical benefits to seeking a single implementation. In order to maximise our computational resources we want to use the minimum number of gates in the simplest configuration possible.
We start by concatenating each individual expression into a single disjunctive expression using logical OR:
$$ ((x \land y) \land \lnot z) \lor ((x \land \lnot y) \land \lnot z) \lor ((\lnot x \land y) \land \lnot z) $$
Next, we look for opportunities to simplify this complex expression. This is similar to simplifying equations in mathematical algebra. It is a heuristic process; there is no formal or automated procedure that will work in every case.
NOT z occurs in each of the individual disjunctive expressions. Therefore we can reduce the repetition by using it only once:
$$ (x \land y) \lor (x \land \lnot y) \lor (\lnot x \land y) \land \lnot z $$
Now we need to consider how we can simplify the remaining expressions:
$$ (x \land y) \lor (x \land \lnot y) \lor (\lnot x \land y) $$
If we look closely we can see that this expression is displaying the truth conditions for OR. The truth conditions for x and y are:
- true if x and y are true
- true if x is true and y is false
- true if x is false and y is true
This recalls our earlier definition of OR:
| x | y | f(x) = x OR y |
|---|---|---|
| 1 | 1 | 1 |
| 0 | 1 | 1 |
| 1 | 0 | 1 |
| 0 | 0 | 0 |
Thus we can reduce:
$$ (x \land y) \lor (x \land \lnot y) \lor (\lnot x \land y) $$
to:
$$ x \lor y $$
The reduction is now complete, allowing us to reduce:
$$ ((x \land y) \land \lnot z) \lor ((x \land \lnot y) \land \lnot z) \lor ((\lnot x \land y) \land \lnot z) $$
to:
$$ (x \lor y) \land \lnot z $$
If we construct a truth table for the original expression (P) and its simplification (P’) we see that they are true under the same logical conditions which demonstrates their equivalence:
| x | y | z | P | P’ |
|---|---|---|---|---|
| 1 | 1 | 0 | 1 | 1 |
| 1 | 0 | 0 | 1 | 1 |
| 0 | 1 | 0 | 1 | 1 |
Constructing the digital circuit
Now that we have reduced P to its simplest form using the connectives AND, OR and NOT we can construct a circuit using the logic gates for these connectives to represent the overall state of affairs expressed by P.
We will have three input bits which correspond to x, y, z, and a single output bit that will reflect the truth value of P based on the inputs. The input bits will be fed into an arrangement of logic gates that that matches the logical connectives in (x OR y) AND NOT z.
We can confirm that the circuit implementation is an accurate representation of P by toggling the input values to confirm that the output is only 1 when either x or y is true and z is false.
Further simplification with NAND
Our circuit uses three different types of logic gate. This is satisfactory but it would better if we could simplify the circuit even further and use a single gate rather than three. To do so we need to further reduce our logic and introduce another type of logic gate: NAND.
NAND stands for “NOT AND” and its truth conditions are the inversion of AND:
| x | y | f(x) = x NAND y |
|---|---|---|
| 1 | 1 | 0 |
| 0 | 1 | 1 |
| 1 | 0 | 1 |
| 0 | 0 | 1 |
NAND returns 1 whenever x and y are not both true. We will represent NAND in our formulae with the symbol:
$$ \tilde\land $$
NAND is a universal logic gate. This means that by using NAND gates and only NAND gates, we can represent the truth function of every other logic gate (AND can be expressed with just NAND gates, as can OR and so on). It follows that we can create every possible logical circuit using NAND gates alone.
Let’s demonstrate this by reformulating (x OR y) AND NOT z with just NANDs:
$$ ( [(x \tilde\land x) \tilde\land (y \tilde\land y)] \tilde\land (z \tilde\land z) ) \tilde\land ( [(x \tilde\land x) \tilde\land (y \tilde\land y)] \tilde\land (z \tilde\land z) ) $$
This is quite difficult to parse, so let’s look at the circuit representation and derive its equivalence to (x OR y) AND NOT z:


