Super Simple ISA
Introduction
We'll look at a very simply ISA (Instruction Set
Architecture) to help illustrate the most basic elements of a machine.
Instructions
This ISA provides only 3 instructions:
Opcode | Operation | Meaning |
0 | ADD a, b, c | Mem[a] = Mem[b] + Mem[c]
|
1 | IMM a, value | Mem[a] = value
|
2 | BNE target, b, c | if (b != c) PC = target
|
Encoding
Our encoding is simple too. Every instruction is exactly 4 bytes long
(one word). The opcode always lives in the first byte and the operands
take up the remaining spaces.
Opcode | Encoding |
ADD | 0 | a | b | c |
IMM | 1 | a | ? | value |
BNE | 2 | target | a | b |
(Note: a ? means that we don't care what's in this particular field.)
If we wanted, for instance, to express the following sentiment: "Add
the value in memory location 18 and the value in memory location 24
and place the result into memory location 15." We could write that
in "human readable" form as follows:
ADD 15, 18, 24
The machine doesn't read instructions like that, so we could encode
the above instruction as follows:
Note that we've used decimal numbers in each position. Because each
location in our instruction is a byte in size, it's often nice to
encode the values in hex:
Or, even more compactly, as a single, 32-bit hex number: 0x000A1218
An Implementation
A machine to implement this instruction set is extremely simple. It
has a program counter (called PC), a memory (called Mem), and adder, a
comparitor and a single register for holding the fetched instruction
(called IR). It just implements the following algorithm:
while (true) {
IR = Mem[PC]
PC = PC + 4
if (opcode(IR) == 0)
Mem[a(IR)] = Mem[b(IR)] + Mem[c(IR)]
else if (opcode(IR) == 1)
Mem[a(IR)] = c(IR)
else if (opcode(IR) == 2)
if (Mem[b(IR)] != Mem[c(IR)]) PC = a(IR)
}
A Sample Program
Here is an example machine program, loaded starting at location zero
in memory:
Location | Values |
0 | 1 | 100 | ? | 0 |
4 | 1 | 101 | ? | 5 |
8 | 1 | 102 | ? | -1 |
12 | 1 | 103 | ? | 0 |
16 | 0 | 100 | 100 | 101 |
20 | 0 | 101 | 101 | 102 |
24 | 2 | 16 | 101 | 103 |
Now, starting with PC=0, hand simulate this program.
Draw a picture of the rest of memory and how it is modified by this program.
What does this program do in English?
Issues/Discussion
Now think about the limitations of this particular ISA. In particular:
- How many memory references are required per instruction?
- The IMM instruction seems to waste a byte. If we made our instructions
variable length, how would this impact the implementation of our
machine? How would it impact the size of our programs?
- Suppose that each memory reference takes 10 units of time, and
that other operations take only 1 unit (like adding two numbers). The
ADD instruction clearly becomes the most expensive operation we've
got, because it performs 3 memory references. Computer clocks run at
a fixed period, and that period has to be long enough to perform the
longest unit of work the machine wants to do. This means that the ADD
instruction essentially determines the length of our clock cycle.
- When the machine executes ADD instruction, it's busy for the entire
clock cycle, but what about IMM? It's sitting around doing nothing...
- Think about how you might improve the situation. Hint: there are
two main approaches: (1) Add more registers to the machine, and insist
that the machine only operate on data in registers. (2) Change the
implementation of the machine so that different instructions can take
different amounts of time.
- Approach (1) will mean adding instructions to the ISA. What kind
of instructions might you add? What is the impact on the size of the
programs? Now it seems like our programs have gotten bigger (more
instructions) -- if we have to execute more instructions it seems like
it should take longer to run our programs. This seems to defeat the
whole reason we took this approach. Or does it?
- Approach (2) will mean a more complicated implementation.
- What is the maximum size of memory in our system? How would you
change the ISA to get beyond this limitation?
- What is the largest value we can add? Again, how would we change
the ISA to handle this situation?
- How much work would it take for us to multiply two numbers?
Would it be a good idea to add an instruction to our machine to do this?
- What other instructions might you add? Hint: Think about common
idioms in C/C++, such as:
i++;
i = x * 4;
i = a[5];
a[13] = ...;
a[i] = ...;
if (x == y) ....
while (p > 0) ...
foo(x, 22);
Can they be easily represented in machine instructions? Think about
the tradeoffs of adding new instructions to an ISA, versus making do
with the instructions that already exist.
dugan@cs.washington.edu