To the left in the diagram, you find the
micro memory containing up to 64 different addresses each with
15 bits (numbered 1 to 15) that we call the micro operations
or MOPs for short. The MOPs control every aspect of the computer.
To the left, you also find the micro PC, which is a
counter register with clear. The width of the micro PC is 6 bits
for a total of 64 different values. The micro PC is controlled by
MOPs number 1 and 2. When MOP number 1 has the value 1, the micro PC
will be cleared after the next clock pulse. Similarly, MOP number 2
will cause a new value to be loaded into the micro PC from the
instruction decoder. The micro PC can also be cleared manually
from the reset button. Since the clr input of
the micro PC takes priority over the ld input, this
will always happen when the reset button is pressed, independently
of the value of MOP number 2.
The final item to the left of the data bus is the
instruction decoder. Its job is to translate instruction codes
to addresses in the micro memory where the micro program for the
instruction starts. The input to the instruction decoder is the low
5 bits of the data bus, and the output is 6 bits wide.
The lower part of the diagram is the arithmetic unit with two
general-purpose registers, R0 and R1. Whether R0 is loaded from the data bus is
determined by MOP number 11. Whether R1 is loaded from the output of
the ALU is controlled by MOP number 12. The
ALU uses MOPS 13 to 15 to control what operation is to be
performed. The inputs (8 bits wide) are taken from the registers R0
and R1 and the output (also 8 bits wide for now) goes to the input
of R1. The output of R1 can be connected to the data bus by MOP
number 8 which is connected to a bus driver (BD) which in effect
closes the connection from the output of R1 to the data bus.
The upper middle part is the
main memory. The enable input is conrolled by MOP
number 3, and its r/w input is controlled by MOP number
4. When MOP number 4 has the value 1, the memory is written. The
address of the memory is taken from the address bus and that date is
output to the data bus (in the case of read) or taken from the data
bus (in the case of write). Thus the data lines of the memory are
bidirectional.
In the middle of the diagram, you find another
register called the address register. It is used to communicate
data from the data bus to the address bus. It is loaded whenever MOP
number 9 has the value 1. The contents of the address register can
be communicated to the address bus through a bus driver conrolled by
MOP number 10. This way, data from main memory or from R1 can be
used as addresses in main memory.
To the right of the diagram, you find the program counter (or PC)
in the form of a counter register with clear and explicit increment. The
clr signal takes priority so that the reset button always
works. If the clr signal is 0 but the ld
signal (MOP 6) is 1, then PC is loaded from the address bus. If
clr and ld are 0 but incr
(MOP 7) is 1, PC is incremented after the next clock pulse. Finally,
if clr, ld and incr are all
0, then PC retains its value after the clock pulse. The value of PC
can be communicated to the data bus through a bus driver by MOP
number 5. This is the way instructions are fetched for execution.
Contents of Micro Memory
Let us now study the contents of the micro memory. The micro
memory is 15 bits wide, one bit for each MOP. We shall organize the
micro memory so that address 0 always contains the beginning of a
micro program to fetch the instruction code at the address in the
main memory stored in PC. Thus, when we start the instruction fetch
micro program, we must make sure that PC points to the next
instruction to execute.
Instruction fetch and decode
As it turns out, fetching an instruction only requires a single
clock cycle. We need to communicate the contents of PC to the
address bus using MOP number 5, we need to read from memory using
MOP number 3. The contents of the main memory at that address will
be translated by the instruction decoder into the address in the
micro memory where the micro program for the instruction starts. We
need to store that address in micro PC using MOP number 2. Finally,
by convention, we increment PC so that when a specific micro program
needs to load instruction arguments, PC already points to the right
place. If the instruction has no arguments, then PC will simply
point to the next instruction. We shall make sure we use this
convention consistently. We now have the entire micro program for
loading and decoding an instruction:
000000: 011010100000000 (fetch)
As you can see, MOPs number 2, 3, 5, and 7 have the value 1, and
all others have the value 0.
Micro program for instruction NOP
We are now ready to atttack the micro programs for the individual
instructions. The first instruction we shall define is NOP (no
operation), which does absolutely nothing. Such an instruction is
sometimes useful for alignment purposes, or for temporary patches to
programs. We shall use instruction code 0 for the NOP instruction.
The micro program for NOP will start at the first available address
of the micro memory, which is 1, since we already used address 0 for
instruction loading and decoding. The contents of the instruction
decoder at address 0 will thus be:
00000: 000001 (NOP)
The micro program for NOP does nothing, so all we have to do is
make sure that after its execution, the contenst of micro PC is 0,
so that the next instruction is loaded. Every micro program for
instructions will be terminated by setting MOP number 1 to 1 for
exactly this reason. With the micro program for NOP, where is the
contents so far of the micro memory
000000: 011010100000000 (fetch)
000001: 100000000000000 (NOP)
Micro program for instruction LDIMM
The next instruction we shall write is called LDIMM (for load
immediate). This instruction takes one argument, so that the entire
instruction is two bytes long. The first byte is the opcode as
usual, and the second byte (the argument) is a value to be loaded
into R0. Such and instruction is useful for loading contstants (i.e.
values that are known at compile time) into registers.
The opcode for LDIMM will be 1 (the first free opcode), and the
start of the micro program for LDIMM will be on address 2 in the
micro memory (the first available address). Our instruction decoder
now looks like this:
00000: 000001 (NOP)
00001: 000010 (LDIMM)
To implement the LDIMM instruction we shall need to address the
main memory using the contents of PC. PC already contains the
address of the argument, since the instruction fetch program
incremented PC after fetching and decoding the instruction code. For
that purpose, we use MOP number 5. We also need to read from main
memory using MOP number 3 and store the value read into R0 using MOP
number 11. By convention, we also increment PC so that it points to
the instruction code of the next instruction. All of this can be
done in one clock cycle, so in the next clock cycle, we need to
fetch the next instruction. To make that happen, we set MOP number 1
to 1. We now have the following contents of the micro memory:
000000: 011010100000000 (fetch)
000001: 100000000000000 (NOP)
000010: 101010100010000 (LDIMM)
Micro program for instruction LD
Loading constants known at compile time is useful, but we also
need to load the contents of variables whose values are now known at
compile time. Such variables correspond to cells of main memory
whose addresses are known at compile time. Thus, we need a load
instruction where the argument indicates an address in main memory,
and that loads the contents of that address into R0.
The first available instruction code is 2 and the first available
address in micro memory is 3, so we now have the following contents
of the instruction decoder:
00000: 000001 (NOP)
00001: 000010 (LDIMM)
00010: 000011 (LD)
The plan for implementing LD is as follows: First use the
contents of PC to address memory (MOPs 5 and 3), and load the
contents into the address register (using MOP number 9).
Simultaneously, increment PC (MOP 7). Then, in the next cycle, use
the address register to address memory (MOPs 10 and 3)) and load the
contents into R0 (MOP 11). As you can see, this instruction requires
two cycles, simply because two different addresses and contents of
main memory need to be used, and that we have only one address bus
and one data bus. By convention, we increment PC as soon as possible
(after the first cycle). Here is the contents of micro memory so
far:
000000: 011010100000000 (fetch)
000001: 100000000000000 (NOP)
000010: 101010100010000 (LDIMM)
000011: 001010101000000 (LD)
000011: 101000000110000 (LD)
Micro program for instruction ST
Just as we need to load the contents of variables, we also need
to store new values into such variables. We therefore need an
instruction capable of taking the contents of a register (here R1)
and storing it into an address given as an argument to the
instruction. This instruction is ST (for store).
As usual we pick the first available opcode and address in micro
memory giving the following contents of the instruction decoder so
far:
00000: 000001 (NOP)
00001: 000010 (LDIMM)
00010: 000011 (LD)
00011: 000101 (ST)
To implement the ST instruction, we have to be a bit more careful
that we have been so far. The reason is that the memory is not
synchronous. We must make sure that the values on the data bus and
address bus are stable before we attempt to write into memory. The
only way we can make sure this is the case is to separate them into
different clock cycles. We also have to make sure the memory gets
the write signal (MOP 4) before the enable signal (MOP 3).
Otherwise, there is a risk that that the memory is read for a brief
moment, which gives conflicting values on the data bus with possible
circuit destruction as a result.
This is thus the plan for the ST instruction: in the first cycle,
load the argument of the instruction (indicating the address to
store into) into the address register, just as with the LD
instruction. In the next cycle, use MOPs 8 and 10 to get the data
bus and address bus stable. In the next cycle, add the write signal
(MOP 4). In the next cycle, add the enable signal (MOP 3). Then in
the next signal, remove the enable signal, but keep all the others
(otherwise, again we may have the same problems). In the next and
final cycle, remove the write signal, but keep the others. Here is
the complete contents of micro memory so far:
000000: 011010100000000 (fetch)
000001: 100000000000000 (NOP)
000010: 101010100010000 (LDIMM)
000011: 001010101000000 (LD)
000100: 101000000110000 (LD)
000101: 001010101000000 (ST)
000110: 000000010100000 (ST)
000111: 000100010100000 (ST)
001000: 001100010100000 (ST)
001001: 000100010100000 (ST)
001010: 100000010100000 (ST)
Micro program for ALU instructions
Load and store instructions are useful, but we also have to do
some arithmetic and logic operations with our data. For that
purpose, we define a set of 8 instructions, one for each possible
combination of values for MOPs 13, 14, and 15. These MOPs simply
instruction the ALU to combine the contents of R0 and R1 in a way
that depends on the MOP values. Recall the ALU codes from
the description of the ALU. For consistency, we use the same
order here. Each of these instructions is going to take a single
clock cycle. We thus get the following contents of the instruction
decoder so far:
00000: 000001 (NOP)
00001: 000010 (LDIMM)
00010: 000011 (LD)
00011: 000101 (ST)
00100: 001011 (COPY)
00101: 001100 (SHL)
00110: 001101 (SHR)
00111: 001110 (ADD)
01000: 001111 (SUB)
01001: 010000 (AND)
01010: 010001 (OR)
01011: 010010 (NOT)
The implementation of each of thes instruction consists simply of
emitting the right values of MOPs 13, 14, and 15, and of emitting
MOP number 12 to make sure the resulting value is stored in R1. Here
is the contents of the micro memory so far:
000000: 011010100000000 (fetch)
000001: 100000000000000 (NOP)
000010: 101010100010000 (LDIMM)
000011: 001010101000000 (LD)
000100: 101000000110000 (LD)
000101: 001010101000000 (ST)
000110: 000000010100000 (ST)
000111: 000100010100000 (ST)
001000: 001100010100000 (ST)
001001: 000100010100000 (ST)
001010: 100000010100000 (ST)
001011: 100000000001000 (COPY)
001100: 100000000001001 (SHL)
001101: 100000000001010 (SHR)
001110: 100000000001011 (ADD)
001111: 100000000001100 (SUB)
010000: 100000000001101 (AND)
010001: 100000000001110 (OR)
010010: 100000000001111 (NOT)
Micro program for JAL instruction
With the instructions we have defined so far, we can not alter
the execution of the program. We can only have so-called
straight-line program. To alter the flow of control, we need a jump
instruction. We shall call this instruction JAL (jump always). It is
what is known as an unconditional jump, and indeed our architecture
so far does not support conditional jump instructions.
As usual, we pick the first available opcode and address in micro
memory, giving the following contents of the instruction decoder so
far:
00000: 000001 (NOP)
00001: 000010 (LDIMM)
00010: 000011 (LD)
00011: 000101 (ST)
00100: 001011 (COPY)
00101: 001100 (SHL)
00110: 001101 (SHR)
00111: 001110 (ADD)
01000: 001111 (SUB)
01001: 010000 (AND)
01010: 010001 (OR)
01011: 010010 (NOT)
01100: 010011 (JAL)
The JAL instruction has one argument, the address to jump to. The
plan for implementing the JAL instruction is as follows: In the
first cycle, load the argument into the address register as will LD
and ST. In the next cycle, use MOPs 10 and 6 to store this value
into PC. While it is not strictly necessary to increment PC in the
first cycle since it is going to be altered anyway in the second
cycle, we do it anyway for consistency. Here is the contents of
micro memory so far:
000000: 011010100000000 (fetch)
000001: 100000000000000 (NOP)
000010: 101010100010000 (LDIMM)
000011: 001010101000000 (LD)
000100: 101000000110000 (LD)
000101: 001010101000000 (ST)
000110: 000000010100000 (ST)
000111: 000100010100000 (ST)
001000: 001100010100000 (ST)
001001: 000100010100000 (ST)
001010: 100000010100000 (ST)
001011: 100000000001000 (COPY)
001100: 100000000001001 (SHL)
001101: 100000000001010 (SHR)
001110: 100000000001011 (ADD)
001111: 100000000001100 (SUB)
010000: 100000000001101 (AND)
010001: 100000000001110 (OR)
010010: 100000000001111 (NOT)
010011: 001010101000000 (JAL)
010100: 100001000100000 (JAL)
|