Nand2Tetris.org Notes

http://nand2tetris.org/course.php

Installing SW:

Install

http://nand2tetris.org/software.php

 

The following are what I did on my laptop running win7.  For other platforms, see instructions at the link above.

 

Download the Nand2tetris Software Suite Version 2.5 zip file to ‘downloads’ directory

 

Move nand2tetris.zip to C:

Extract zip file to C:\ – creates a directory C:\nand2tetris

 

run C:\nand2tetris\tools\HardwareSimulator.bat

 

Hardware Simulator Tutorial: HardwareSimulatorTutorial.pdf

 

Simulator Hints

 

Animate tab must have either program flow or program & data flow selected in order to change input pin values.

Not possible to change Part pin values

Run | reset -- to reset everything on the simulator

Changing an input pin value enables the eval button.  An eval is done only if some input has changed.  Tick or tock will also do an eval if some input has changed.

 

Breakpoints

Click the checkmark every time after a breakpoint is hit.  Must have a value as well as a pin name.  Add a breakpoint for 1 and one for 0 if you want to stop every time a pin changes.

Notes:

1.    These ‘breakpoints’ are really watchpoints.  You won’t stop every time a pin is read, only when it changes.

2.    Notice what a ‘breakpoint’ means is the script pauses.  The smallest command a script can execute is eval, which executes the entire chip logic. 

 

Introduction Lecture:

Ground rules: http://nand2tetris.org/terms.php - basically don’t post solutions to N2T problems and don’t look too hard for answers.  Do your own work.

 

notes/N2Tlecture00introduction.pdf

Slide 25

Note the direction of the diodes in the CMOS image.  Pointing to the right means allow current to flow through the transistor when the input is low and do not allow current to flow when the input is high.  Pointing to the left means allow current to flow through the transistor when the input is high and do not allow current to flow when the input is low. 

 

The following NMOS gate described at http://en.wikipedia.org/wiki/NAND_gate is easier to understand than what is in the lecture:

  

If either a or b is low, current cannot flow to ground, so F is high.

If both a and b are high, current can flow to ground, so F is low.

 

 

HDL is a declarative language.  It specifies a wiring diagram, which the simulator builds in its entirety then allows you to run.

 

Boolean Logic Lecture (Chap 1, Appendix A):

Lab1 Assignment (with reading link): https://www.nand2tetris.org/project01

Slides: notes/N2Tlecture01BooleanLogic.pdf

 

Functions take multiple inputs and produce one output.

Boolean functions are written as truth tables.

One column for each input and one column for the output.

A function’s truth table shows all of the possible inputs and outputs.

Alternate notation you sometimes see

Logical operators are:

Negation (¯) == the ‘not’ function.  Notice the superscript can stretch over an entire expression, e.g., Nor == Not(x + y)

Conjunction (·) == the ‘and’ function

Disjunction (+) == the ‘or’ function

 

Precedence:

The three operators given above have three distinct precedence levels

1.    ¯   (often written !a – meaning not(a))

2.    ·    (often written with juxtaposition ab – meaning and(a, b))

3.    +

 

Parentheses may be used to alter precedence.  Notice when the negation ‘line’ is stretched over an expression containing an operator, it means the entire expression is parenthesized and the result is the negation of the parenthesized expression, as shown above for the Nor function.

 

Example from the table on this slide:

 

With Precedence Rules

Without

 

(x · Not(y)) + (Not(x) · y)

((x · Not(y)) + Not(x)) · y

Xor

0 1 1 0

0 1 0 0

 

Writing expressions in functional (prefix) notation:

Xor(x, y) == or( and( x, not(y)), and( not(x), y))

 

Functional (prefix) notation eliminates all precedence problems, because it forces you to include all parentheses.  It is not used by nand2Tetris, because it primarily uses the operators shown above, which use infix notation.

 

In the book on p.10, we see:

x Or y == (x Nand x) Nand (y Nand y).

 

The book’s example is in infix notation -- the function name is in the middle of the operands.  Notice with infix notation, the parentheses are still needed. 

 

x

y

(x Nand x) Nand (y Nand y)

((x Nand x) Nand y) Nand y

0

0

0

1

 

Equivalent prefix notation is:

 

x Or y == Nand(Nand(x,x), Nand(y,y))

x

y

x Or y

Nand(Nand(x,x), Nand(y,y))

0

0

0

0

0

1

1

1

1

0

1

1

1

1

1

1

 

Example: Equivalence(x, y) == ?What is the truth table?

 

Does this make sense to you intutitively?

Slides 14 - 23:

A little Boolean algebra – useful for circuit simplification:

DeMorgan’s Theorem:

not(and(x, y)) = or(not(x), not(y))

not(or(x, y)) = and(not(x), not(y))

 

I like to think of this as the ‘distributive property of the not operator’.

 

In infix notation:

b == !(!a + !b)

 

From: http://www.allaboutcircuits.com/textbook/digital/chpt-7/boolean-rules-for-simplification/

Here is a summary set of useful Boolean laws.

(Retrieved from http://www.elec-intro.com/EX/05-14-10/NF130220.GIF in October, 2015.)

 

 

Factoring:

Just like in arithmetic where or() (+) is addition and and() (·) is multiplication:

 

a · b + a · c == a · (b + c)

 

Here is a more comprehensive set of Boolean Algebra Laws.  Retrieved 10/6/2015 from http://www.elec-intro.com/EX/05-14-10/NF130220.GIF

 

 

 

Dense Boolean algebra summary from canonical form through some simplification ideas:

https://courses.cs.washington.edu/courses/cse370/00wi/sections/qs2.pdf

 

Karnaugh maps:

https://www.allaboutcircuits.com/textbook/digital/chpt-8/introduction-to-karnaugh-mapping/

 

 

Yields 1 exactly when B is 1, so the function = b

 

Slide 33

Cannonical form is what we call the collection of and, or, not produced using the technique shown in slides 26 - 33.

Slide 35

 

Choose all possible values for ‘a’ and demonstrate to yourself that Not(a) = Nand(a,a)

 

Prefix Notation

if you can express the function you want in functional (prefix) notation, you can easily build a circuit the same way the java VM evaluates a method call – first evaluate the arguments, then ‘pass’ the arguments to the method:

 

xor(x, y) = or(and(x, not(y), and(not(x), y))

x and y are given, i.e., they come from outside the circuit

make y the input of not() – call the result of not(y) ‘ny’  (you may use any name you like, but this one makes sense)

connect x and ‘ny’ to and() – call the result ‘axny

and so on

 

Slide 47

All chips have inputs and outputs.

The And chip has 2 wires entering the chip, which are named ‘a’ and ‘b’, and 1 wire leaving the chip, which is named ‘out’.

The Nand chip also has 2 input wires named ‘a’ and ‘b’ and 1 output wire named ‘out’.

 

In the file And.hdl, the expression:

Nand(a = a,

            b = b,

            out = x);

means:

Nand(connect Nand input wire a = to And input wire a,

            connect Nand input wire b = to And input wire b,

            connect Nand output wire out = to internal And wire x);

 

The identifier on the left is the name of the input or output wire on the chip being ‘soldered in’, and the identifier on the right is the name of the wire defined on the chip being ‘soldered onto’.

Introducing a new identifier on the right creates a new internal wire on the chip being ‘soldered onto’.

 

Project 1 Hints:

·         The HDL survival guide (notes/HDLSurvivalGuide.html ) shows the names of the input and output pins of all the chips you need to build.

 

·         The only gate you have to start with is Nand(), which has two inputs.

·         Make truth tables showing all input and output pins

·         Not – has only 1 input, but you have to build it with something that takes 2 inputs.

 

Think of inputs as physical wires, because that’s what they are.

Every wire has a variable name.

If you use the same name twice, electrically, you have spliced two wires together.  In the following picture from the lecture notes, pin ‘a’ is spliced and goes 2 different places.  There is nothing that prevents you from making these 2 places be the two input pins of the same gate.

 

Slide 86

Slightly older image than in this slide, but the same chip.  It is easy to visualize how the chip needs to be structured from the prefix notation given below the chip implantation:

 

Boolean Arithmetic Lecture (Chapter 2):

Reading and Assignment: https://www.nand2tetris.org/project02

Slides: notes/N2Tlecture02BooleanArithmetic.pdf

 

Hexidecimal (or Hex) numbers

Base 2 (binary) has two digits: 0, 1

Base 8 (octal) has eight digits: 0, 1, 2, 3, 4, 5, 6, 7

Base 10 (decimal) has ten digits: 0, 1, 2, 3, 4, 5, 6, 7, 8, 9

Base 16 (hexadecimal) has 16 digits: 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, A, B, C, D, E, F

 

Manipulation of quantities in any base uses the same techniques demonstrated for binary above

Addition in hex

55D + 52 D == 107D

 

37H + 34H == 6BH

 

37H == 3 * 161 + 7 * 160 == 55D

 

34H == 3 * 161 + 4 * 160 == 52D

 

6BH == 6 * 161 + B * 160 == 107D

 

 

Full Addr

Truth table and Canonical representation:

 

 

This picture implements the reduced canonical representation:

 

 

 

Slide 10 - ALU

1.    Modifies the inputs according to the zx, nx, zy, ny bits to produce 2 16-bit internal busses (I’ll call them xPreset, yPreset).

2.    Adds or Ands (according to the f bit) xPreset, yPreset to produce initial output (I’ll call this outInit)

3.    Modifies outInit according to the no bit to produce out

 

 

Notice the preset step takes x and y as inputs and outputs one of 2 choices.  Sounds like a Mux to me …

 

x! means Not(x)

 

Reference text book p. 37:

 

The ALU produces Not(x) when:

zx

nx

zy

ny

f

no

out=

0

0

1

1

0

1

!x

 

Suppose x == 0011, y = 1010  (only 4 bits instead of 16 for the example)

zx is 0 (false), so pass x through unchanged (px = 0011)

nx is 0, so pass x through unchanged (px1 = 0011)

zy is 1 (true), so py = 0000

zy is 1, so py1 = 1111

f is 0, so and (px1, py1) o = 0011 

no is 1, so not(o)  out=1100

 

what should the values of condition codes zr and ng be?

The most significant bit (MSB) of the output 1100 is 1, so ng should be 1.

The output does not equal 0, so zr should be 0.  Hint: Use And16.

 

Notice the zx, nx control bits need to be set after the output is calculated.

 

Nand2Tetris HDL allows you to specify the constants true and false as values to an input pin. 

 

Slide 11 - ALU

An if can be implemented as a mux, e.g., to implement the zx functionality, you can use a 16-bit mux, which has the 16 x input bits as ‘a’, 16 bits of false as ‘b’, and the zx input is the select input to the 16-bit mux.

 

An And16 could also be used to implement zx.

 

A handy hdl ‘trick’:

…(a[0]=true, b[0]=false, …)  sets the low order bit of bus a to 1 and the low order bit of bus b to 0.

 

The following NOT COVERED: 

Sequential Logic Lecture (Chapter 3)

Slides: notes/N2Tlecture03SequentialLogic.pdf

Reading Links: https://www.nand2tetris.org/project03

Sequential vs combinational

Clock

The oscillator drives the clock signal low for a period of time, then high for the same period of time, and repeats.

 

The clock in a 2GHz processor cycles the clock signal 2 billion times in 1 second.

 

Demo builtin/DFF.hdl and clock and default script.

 

Try script:

load DFF.hdl,

output-file DFF.out,

output-list time%S1.4.1 in%B2.1.2 out%B2.1.2;

 

repeat {

    tick,

    output;

    tock,

   output;
}

 

 

NOTE:

The timing diagrams start low (in the ‘tick’ phase).

The tick command in a test script causes the clock signal to go high (to the ‘tock’ phase)

The tock command sends the clock low (to the ‘tick’ phase)

 

Before a tick event, the simulator displays an integer time, e.g., 0,

Before a tock event, the simulator displays a ‘+’ time, e.g., 1+

 

So, the tick event happens at the END of the tick phase (the tick event causes the clock signal to go high).

Circle this spot on clock signal diagrams.

 

Timing notation

out(t) = in(t-1)  means the signal on the out wire at time t is the same as the signal that was on the in wire at time t-1.

 

A time is when a tick event happens.  References to a particular time, e.g., t or t-1 in chip specifications refer to some particular tick event.

 

We call the time the first tick event happens t(0), the time the second tick event happens is t(1).

 

Show this behavior in the simulator using the built-in DFF chip

 

All DFFs in the system receive the clock signal at close enough to the same time that they act as if they are synchronized. 

 

In order for the whole system to be synchronized, the clock oscillator has to be slow enough, i.e., hold the clock wire high or low long enough for an electrical signal to move between an output pin of one chip to an input pin of any other chip it is connected to.

DFF

The effect of a DFF is to keep the input value from passing until the clock signal goes high.

From chapter 3 of the book:

 

The book says: “… it is not clear how we’ll ever be able to load this device with a new data value, since there are no means to tell the DFF when to draw its input from the in wire and when from the out wire.” 

 

I object to “draw its input from the in or out wire.”  If in and out are connected by a bare wire, as they seem to be in this diagram, they will always have the same signal, because current would flow from one to the other without resistance.  If in goes high, current will go through the wire to out.  If out goes high, current will go through the wire to in, so in will also go high.

 

Nand2Tetris provides a primitive DFF, but we know all logic circuits can be built from nand gates.  http://www.marksmath.com/tecs/ is an interesting link that does this.

 

 

Show two DFFs in sequence (03/DFF2.hdl) – takes 2 clock cycles to get through.

 

Bit vs DFF

Use the simulator to show builtin Bit.hdl and compare with DFF.

 

To an output wire, so it can be used as an input to another PART in the chip, use two out= ‘parameters’, e.g,:

 

    DFF(in=mo, out=out, out=outDFF);

 

Bit specification:

if load(t-1) then out(t)=in(t-1)

else out(t)=out(t-1)

 

Remember, time t is when the clock ticks, and the clock ticks at the END of the tick (low) phase.  So, whatever is on the ‘in’ and ‘load’ pins at the time the clock ticks, the output generated from this input will be available on the output line at the time of the next clock tick.  What is on the lines at any other point in the clock cycle is not guaranteed.

 

Here is the Bit script:

Load Bit.hdl,

...

set in 0,

set load 0,

tick,     // clock signal goes high t(0)

output;

// displays | 0+   |  0  |  0  |  0  |

 

tock,    // clock signal goes low

output;

//| 1    |  0  |  0  |  0  |

 

set in 0,

set load 1,

tick,  // t(1)

output;

// | 1+   |  0  |  1  |  0  |

 

tock,

output;

// | 2    |  0  |  1  |  0  |

 

set in 1,

set load 0,

tick, // t(2)

output;

// | 2+   |  1  |  0  |  0  |

 

tock,

output;

// | 3    |  1  |  0  |  0  |

 

// the output showed what the inputs were before the tock. 

 

set in 1,

set load 1,

// The inputs were changed after the last display, but BEFORE the next tick,

// but there was no output of these changed inputs until AFTER the next tick,

// which makes the cmp file look like the load was off at t(3), but, as we see here, it was on at t(3)

 

tick, // t(3)

output;

// | 3+   |  1  |  1  |  0  |

 

tock,

output;

// | 4    |  1  |  1  |  1  |

// output == 1 AFTER the tock.  In other words,

// the output did not change to 1 until the clock signal was in the low

// phase, so output was still 0 when the clock was in the previous high phase.

 

set in 0,

set load 0,

tick, // t(4)

output;

// | 4+   |  0  |  0  |  1  |

// since load == 1 at t(3) and in == 1 at t(3), out == 1 at t(4)

 

BitTiming.pdf is a color coded spreadsheet showing this run.

 

 

Ram8

The RAM8 chip has a GUI.

The GUI is showing the values inside the RAM chip. 

The out pins show are not always the same as the internal values.

 

* Memory of 8 registers, each 16-bit wide. 

 * The chip facilitates read and write operations, as follows:

 *     Read:  out(t) = RAM8[address(t)](t)

 *     Write: If load(t-1) then RAM8[address(t-1)](t) = in(t-1)

 

In words: the chip always outputs the value stored at the memory location specified by address. If load == 1, the in value is loaded into the memory location specified by address.  This value becomes available through the out output starting from the next time step.

 

Unfortunately, Read:  out(t) = RAM8[address(t)](t) is not a realistic implementation, as some time is required to read what is in the RAM, even if it is a very small time.  It is not possible to ‘read’ an address, A, on the address input lines and simultaneously put the value stored at this address on the output lines.

 

In the simulator:

on both tick and tock events, the value in the RAM chip at the address on the address[3] pins is put on the output pins, but it really matters only on the tick event.

 

On writes, if ‘load’ = 1 and ‘in’ = 5 when the ‘clock’ is displaying an integer, e.g., 1, then the earliest time this 5 could be present on out lines is when the ‘clock’ is displaying 2.

 

Cost vs Access Time

Access time increases as you go down

Cost increases as you go up.

 

SRAM does not have to be refreshed as often to hold its value.

DRAM has to be refreshed more often.

More refreshing takes more time.

Project 3 Hints:

Use built-in chips from previous chapters -- there is a warning on the bottom of p. 54 in the book.

 

Look at the .cmp files to review what inputs should produce what outputs.

 

Error: can’t connect gate’s output pin to part:

Workaround: instead of sending value to output, send it to a new intermediate name (new internal wire)

 

Consider implementing a RAM2bit :

IN in, load, address  // each 1 bit

OUT out                     // 1 bit