Comp 150-001, TTh, 11:00-2:00, DH-339

Class 4

Midterm is Tuesday, June 9
Some sample exercises

Virtual memory

Not all the computer memory you use is necessarily there. Typically there is physical memory, the installed RAM with addresses from 0 up to some maximum (eg 231-1, for 2 GB, or 230-1 for 1 GB, or 220-1 for 1 MB). However, physical memory is then divided into blocks, or pages, typically 4KB each (12 bits' worth; 12 bits are needed to specify the position of a byte within a block). Each individual process is allocated however many blocks it needs, and the addresses are "remapped" on the fly so that the "virtual" blocks can be contiguous (or can be two or three widely separated contiguous blocks), but the physical blocks can be allocated in any manner.

For 32-bit addressing, the 4KB block size means the low 12 bits are left alone; they refer to the position within the block. The upper 20 bits specify the block, and special hardware replaces the virtual block number with the appropriate corresponding physical block number. (draw picture)

One of the main practical advantages is that a process can ask for memory only as it needs it, all the while other processes are also asking for memory in dribs and drabs, and yet memory does not end up fragmented: each process sees memory added contiguous with its previous allotment.

Once upon a time, pages that had not been used in a while could be written to disk, so as to expand the apparent physical memory by a great deal. However, with RAM so cheap today, this isn't necessarily worth the effort.

Computers and Electricity

How do we represent bits with electricity?
simple gates: not, and, or, xor, nand, nor
truth tables
or gates are not trivial! Though they would be if electrons were more cooperative....
general combinatorial circuits: anything that has a set of inputs and the output can be represented as a truth table.

D-shape: AND
concave at left: OR
Circle at outbound end: NOT (used in NAND, NOR, etc)
XOR: second concave line at left.


transistors!
Simple inverter: one transistor, and one resistor: book diagram, p 100/101
Slightly more complex: two transistors in series, of opposite type (PNP, NPN).
If the input is high, T1 closes and T2 opens. Output connects to gnd, and is low.
If the input is low, T1 opens and T2 closes. Output connects to Vcc (positive voltage), which is high.

                    Vcc
                     |
                +---T1

                |    |
-------input----+    +-------output
                |    |
                +----T2
                     |
                     gnd

This is from http://en.wikipedia.org/wiki/Inverter_(logic_gate)
This circuit is made of "CMOS" transistors, one PMOS (upper, with the bubble; closed (like a switch) when the gate voltage A is zero) and one NMOS (lower, open when A==0).  In the circuit here, when A==0, then the upper PMOS transistor is closed and the lower is open, and so Q is connected directly to Vdd, and is thus high. When A is high, the upper transistor opens (disconnects) and the lower one closes, connecting Q to Vss = ground, and so Q is low.



More information on CMOS can be found at http://en.wikipedia.org/wiki/CMOS.
Discusson of "real" inverters can be found at http://www.allaboutcircuits.com/vol_4/chpt_3/2.html.


Half-adders: 1-bit
    input: two bits
    output: sum bit and carry bit (sum is XOR, carry is AND)

Adders: 1-bit
    input: three bits; two data bits and the carry-in bit.

8-bit adder: eight 1-bit adders, cascaded
ripple-carry
carry look-ahead

Multiplexers (muxes)

Fig 4.11 of Dale & Lewis, p 107



S-R latch: beginnings of memory.
Note that, due to the feedback, this is not a combinatorial circuit.

Most people make SR latches out of a pair of NOR gates. Our book uses NAND gates. The result is that the gate works "backwards": 1 represents quiet, and 0 represents do something. The forbidden state in our book is (0,0). You set S or R (but not both!) to 0 to update the latch state.

The books' version is typically denoted SR-with-a-bar-overhead, or inverted-SR latches. Inverted latches are easier to build, because NAND gates are slightly cheaper (in terms of component count) than NOR gates.

S=R=1: saved-data state

S=0,R=1: SET state: set to 0

R=0,S=1: RESET state

S-R latches are used for registers and other static RAM applications.

Gated SR latch: add some gates in front (two AND gates in parallel, with their outermost inputs being S and R, and the pair of innermost inputs being the Enable line), so that if Enable is set (1) then S or R can write, but if Enable is 0 then nothing happens.

Flip-flop: the Enable line is clocked, 0-1-0-1-0-1-0-1-...
SR flipflop: straightforward gated SR latch
D-flipflop: R = not S. The S input is renamed D. If D=0 when enabled, we write 0; if D=1, we write 1.


Dynamic ram: each bit is stored in a tiny capacitor, with a transistor for refresh. The whole DRAM unit is "strobed" peridoically (every few milliseconds) to refresh it.

To read a bit: everything is in a square array, say 32 x 32. There are 32 horizontal wires, 32 vertical wires, and 1024 bits in all, one bit at each intersection of a pair of wires. To read a bit, you turn on those two wires.

clocking:
how do we get to a machine that does things in sequence?
The clock signal tells the machine to go to the next step; the clock pulses are spaced far enough apart that each step "settles down" (ie completes) in one clock pulse.

Some steps in the development of synchronous operation:

    Counter:            
    Each time clock input goes from 0 to 1, output binary number increments by 1
    (0001 is added to output value)

Basic counter:
Low-order bit is toggled 0,1,0,1 by the clock (actually, half the clock speed)
Every other bit looks at its predecessor bit. If the predecessor bit goes from 1 to 0, they must change: from 1 to 0 or 0 to 1. 0->1 is just incrementing; 1->0 means that the subsequent bit will also change.

    Attach a counter to a selector:
    Each time input goes from 0 to 1, a new output line is activated:
                  +--------------
    --------------+--------------
                  +--------------
                  +--------------

Basic model:
on each clock 0->1 transition, computer does ONE STEP.
Settles down afterward, & waits for next transition

Microcode steps:

ALU: do all operations, have a demultiplexor select which one, based on the opcode



Python

Once you define a function, it stays defined.
Functions are your PROGRAM, in essence.


while loops, continued

Watch the indentation! Idle helps! Tabs help.

loop to reverse a string S. The reversed string will be in the variable revS, at the end of the loop.

def strreverse(S):
i = len(S)-1; # last position of S
revS = ""
while i>=0:
revS = revS + S[i]
i = i-1 # this time i is DECREMENTED
return revS

Some people feel the following loop with identical functionality is more elegant:

def strreverse(S):
i = len(S);
revS = ""
while i>0:
i = i-1
revS = revS + S[i]
return revS


Printing a triangle (can we make the number of rows arbitrary?)

def triangle():
    row=1
    while row<=10:
        col=1
        while col<=row:
           print '*',
           col=col+1
        print                  # start a new line
        row+=1

How about if we rewrite that inner loop as its own function?
def printrow(len):
    col=1
    while col<=len:
       print '*',
       col=col+1
    print

This makes it possible to simplify triangle:
def triangle2():
    row = 1
    while row<=10:
       printrow(row)
       row+=1

Greatest common divisor, as a python function. Note that each time through the loop, either a or b gets smaller! But there is no explicit "index" variable, such as i.

def gcd(a,b):
    while a>0 and b>0:
       if a>b:
            a=a-b
       else:
          b = b-a
    return max(a,b)
  

Infinite loops: try leaving off the row+=1 in triangle()


types and type checking