CLOCK UNIT

Below is a snapshot of the clock unit designed in Lab1. It consists of a standard LogicWorks clock unit producing a high signal of 10 and a low signal of 240 units of time, extended into a chain of seven buffers each of delay 30. This way we can produce the proper pattern of subcycles as pictured in the time graph below.


GENERIC 12-BIT REGISTER (MAR)

The next item that needs to be constructed is a generic 12-bit register with a 3-state SEND line. While LogicWorks does not have a handy one, one can easily be constructed with the available pieces as follows:

This generic 12-bit register wears many hats, such as MAR (Memory Address Register) and PC (Program Counter). We need to create a system where during the clock's first subcycle (SC0) PC passes its contents to MAR. Up until this point, we do not need them to be different from each other. That is, we just need two 12-bit registers to pass information from one to the other via the 16-bit bus when the clock ticks. However, as the main program counter, the PC has to have some added functionality. We must add to it the ability to increment its information with each clock tick. Specifically, we want PC to increment its contents by 1 on the clock subcycle immediately following the passing of the information to the MAR, i.e. SC1, or the second subcycle.

To do this, we need to construct a PC+1 register. Obviously, it will be based on the generic 12-bit register (PC), but the functionality we need from the PC+1 means we have to add some pieces to it. Before we get to the point where we can offload the incrementing portion of this to the ALU (to be constructed much later), we can use something more basic to perform this for us. A full adder comes to mind. We can construct a full adder from several half-adders, as follows:


BASIC HALF-ADDER


COMBINING 12 HALF-ADDER DEVICES


We want the generic 12-bit register at the core of this PC+1 device to be able to send out its information (but only at our behest) as well as to loop its own information back to itself (incremented by 1). We therefore need to wire the output of the 12-bit to a 3-state buffer that holds it until we say SEND. Meanwhile, we split the output lines of the generic register to go to our full adder, which will take the 12 bits of information, add 1 to it, and send it back to the 12-bit register via some MUX devices.

Thus modified, the register can accept an ADD1 command. When the mysteriously named MUX_S0 line receives a signal, the internal 12-bit register is incremented by 1, as is the overall Program Counter device. We represent this as:

PC=PC+1

 

Click on image to zoom in.

We will call this configuration of PC+1 and MAR (connected via a 16-bit bus) our SKELETAL PROCESSOR. (PC -> MAR will occur at subcycle 0.)

 


In order to upgrade our skeletal processor, we need to create a basic 16-bit register we will call IR, or Instruction Register:

Now we can create a PROM, or Programmable Read-Only Memory device. A PROM can be created and recreated to store a sequence of commands to be executed by the processor.

At subcycle 0, the PCA device sends information to the bus, and a magical 3 time units' delay later, the same subcycle causes the MAR to store (LOAD) this information off the bus. Since the MAR signal is permanently set to 1, this address is passed to the PROM device right away. The PROM is designed to take an address, treat it as a line number or as the number (in sequence) of one of its stored instructions. Therefore, it translates the given address into a corresponding pre-programmed instruction, and then sends it off to the MBR. We wire MBR to receive this info still as part of subcycle 0, but 3 delay units after the MAR device was loaded. This helps assure a proper sequencing pipeline of information being passed:

Another piece we will add to this is the MBR device, which is simply a buffered 16-bit register:


BASIC 16-bit REGISTER WITH LOAD, SEND, CLEAR, AND SEND-INDEPENDENT Q15 OUTPUT LINE

The reasons for providing a send-independent Q15 output line will become clear toward the end of the project.

We want the contents of MBR (the instruction received from the PROM) to sit and wait until subcycle 2 comes along and releases it to the bus. The MBR device's buffer is our ally in this respect. When the SEND line finally releases the instruction, the only device we want listening for this information is the IR. The IR loads this information 3 delay units after MBR sends it (during subcycle 2).

Now the information is stored in the IR. It's important for the IR to retain this info (at least momentarily) to be able to provide it to the MACH (Machine Decoder), as well as to a couple other devices we will discuss later. The MACH device is a basic 4x16 decoder that deciphers the upper four bits of the instruction (***CALLED WHAT?***) freshly arrived into the IR into one of sixteen singular, mutually exclusive signals, each used to initiate a different process.


MACHINE INSTRUCTION ( 4X16 ) DECODER

We will also create a RUN flip-flop, with a Power ON switch and a HLT switch according to the following spec:

INPUTOUTPUT
HLTpowerONRUN
*11
*1->01->1
0->101->0

Pictured below is the most basic functional RUN flip-flop that follows the spec exactly, with just the simplest of D-type flip-flops as its core:

We want to be able to stop everything by sending a HALT signal to the RUN flip-flop if and when the four bits received by the MACH device are 0000. To accomplish this, we will be connecting ouput line Q0 of MACH to the HALT line of the RUN flip-flop, because Q0 is the Machine Decoder line that sends a signal out when 0000 is decoded:


Now that we have taken care of the operator part of the IR's data, it's time to take care of the bottom 12 bits it sends out, also known as the operand. At the same time, we want to keep the data ready in 16-bit format. To accomplish our goals, then, we need two more devices: a basic 12-bit register modified to have only a SEND line (no LOAD or CLR); and a ZERO flip-flop. The latter will send out 0000 (and only 0000) at the same time to complement the other 12 bits (to form a full 16-bit address in the form: 0000 #### #### ####).

The SEND line of these devices comes from subcycle 3's LDG (LOAD GReg or LOAD General Register) operation freshly decoded by MACH:


LDG_C3 loads Greg during subcycle 3, not simply based on SC3 ANDed with Q6. We need to coordinate the passing of information to and from all the previous registers and devices, using, in this case, a number of OR gates with zero-delay:

We also want the ability to LOAD GReg on subcycle 4. Here's how we wire LDG_C4 to GReg and to the other devices:

We will now test the wiring of LOAD G by creating a new PROM and hardcoding the following into it:

ADDR. INSTR. ACTION
0000 FFFF nop
0001 6004 ldg[004]
0002 0000 hlt
0003 0000 hlt
0004 BBBB data "BBBB"

This program does nothing on the first line. Then, on the second line, it says 6004, or "send a signal out of MACH's Q6 line (to LOAD G) and load G with whatever PROM has stored on line 004. On that line, the PROM has stored BBBB—something that has no particular meaning to the decoder, so it is just treated as "data". So, basically, we're storing this data in PROM's address number 004, and we're calling the data contained at this address on line 1 in the PROM.

As such, once the program stops running, the heretofore empty GReg should have BBBB stored in it. And...it does:

We need to add/connect a couple more gates in anticipation of the next step:

The next step is to upgrade the Program-once, Read-Only-Memory-thereafter device by creating a 4K RAM device, which can be reprogrammed and is therefore more flexible. The test program is identical to the test program in the PROM, we are merely replacing devices. Therefore, as expected, the end result is the same: GReg ends up storing BBBB as its data. There is one other consideration, however: the input lines of RAM require a certain amount of rewiring, as follows:

Now we can demonstrate the real power of RAM as accessible memory-space. We will show that programmed code can "self-correct" by having RAM cause the execution of a command that ultimately results in RAM being rewritten.

To do so, we program a RAM device to contain the following code:

ADDR. INSTR. ACTION
0000 FFFF nop
0001 6100 ldg[100]
0002 7101 SDG [101]
0003 0000 hlt
0100 ABCD data "ABCD"
0101 0000 data "0000"

6100 means Q6 (load G) with contents of memory address 100 (currently "ABCD").

7101 means Q7 (send G) to the memory address 101 (currently "0000").

Then we halt. The program should have "copied" the data from memory address 100 into memory address 101. Memory address 101 should now contain "ABCD"—he same data as 100. The RAM device has now rewritten itself.

The last thing we do is to combine RAM's ability to rewrite itself with the ability of and Arithmetic Logic Unit (ALU) to process information provided by RAM.

To construct an ALU capable of handling 16 bits of data thrown its way we build incrementally, up from the most basic, 1-bit ALU:

From there, we build a 2-bit ALU:

...and a 4-bit:

...8-bit:

...and finally, 16-bit ALU:

Once we have the ALU, we wire it up to the existing structure as follows:

Finally, we have the whole circuit ready for the big test. We program the RAM according to the following spec:

ADDR. INSTR. ACTION
0000 FFFF nop
0001 6100 ldg[100]
0002 7200 SDG [200]
0003 6101 ldg [101]
0004 7201 sdg [201]
0005 1200 add [200]
0006 7202 sdg [202]
0007 0000 hlt
100 ABCD data "ABCD"
101 1234 data "1234"
200 0000 data "0000"
201 0000 data "0000"
202 0000 data "0000"

We run this, the ALU adds the two values in RAM memory address 100 and 101 and places them back in RAM at address 202. ABCD + 1234 = BE01. Therefore BE01 is what should display as the last line of the RAM device.

And indeed it does.

And the registers lived happily ever after...