Lecture 2: ISA – Storage and Addressing Models
This is the beginning of a series of notes on the ISA – Instruction Set Architecture. The ISA is an architecture that defines the interface between compilers, and the hardware. It is the connection layer between microarchitecture (hardware implementation of ISA), and the compilers.
For example, if making a robot whose only function was to follow you around, what instructions would you want? Here are a few ideas:
forward(speed)
back(speed)
turn
stop
run
There are a few things to note here:
- ISA instructions can define operands, such as the operand
speed
for the forward
command.
- ISA instructions can be somewhat overlapping in function - the
forward
and run
commands are pretty similar in purpose, but one allows you to specify the speed at which it runs. The choice to use more, overlapping instructions for specific purposes, instead of fewer, more general commands, is actually an active debate!
ISA Design – Freedom and Choices
ISA design focuses on 3 main aspects:
Functionality
- Arithmetic functions:
add
, multiply
, divide
, sqrt
- Some of these may be overlapping in function, such as multiply and divide. However, hardware implementation of these functions may differ, so those operations are more efficient.
- Branching: Redirecting the flow of execution of code to different paths.
- This happens in
if
statements, but also conditionals in for
statements and function calls.
- Load/store: Moving data from one place to another. Usually involves moving information from the processor registers to the DRAM.
- The processor registers are memory storage units directly in the processor, that allow for extremely fast read/write times. However, in most computers, the registers only have memory to store about 16 integers.
- The DRAM, on the other hand, can store much, much more, but the read/write speed is on the order of magnitude of hundreds of clock cycles.
- The compiler usually does the optimization of whether to store a variable in registers or DRAM.
mmx_add
- higher level vector addition operations, not covered in this EECS 370.
Storage
- How many registers should be available?
- How much memory should be available in DRAM?
- What is the architecture of the storage?
Operands
- How should instructions be formatted?
- Can you have 0, 1, 2, or more operands?
- How are immediate operands handled?
Side note: Immediate operands are constants in the instruction code, rather than using a variable such as a register.
Instruction Encoding
RISC vs CISC
- A RISC instruction set is a Reduced Instruction Set Computer.
- An instruction set with fewer, more simple commands for general purposes.
- Benefits: More efficient instructions
- Drawbacks: Less specialized commands for programmers; worse backwards compatibility.
- Examples: ARM, LC2K (what we emulate in EECS 370)
- A CISC isntruction set is a Complex Instruction Set Computer.
- Philosophy: More commands are better. "Should we need include a
sqrt
function? Why not?"
- Benefits: More specialized commands for programmers, better backwards compatibility.
- Drawbacks: Reduces efficiency of each instruction.
- Examples: x86 (which we use today!), UAX
In specialized instruction sets, programmers are actually given a CISC instruction set which is internally converted to a RISC instruction set (with the necessary overhead, of course).
Translation of Software into Machine Code
First, you have a C program. The C program is then compiled into assembly code. This produces platform-specific instructions. For example, while iPhone compilation produces ARM assembly code, Intel compilation produces x86 code.
To see the assembly code produced from a C program, use:
gcc -S filename.c
This produces a file, filename.s
, which contains the assembly code.
Assembly Code
Consists of instructions, with the following fields:
- Opcode: A name of the instruction to perform. Similar to a function name in programming.
- Source (input) operand: Which registers or immediate values are input to the instruction.
- Destination (output) operand: Which register to store the final value in.
Example:
add R2 100 R1
This adds 100 to the value of the register R1
, and stores the final result in R2
.
Example
The program is as follows:
add R3 R1 R2
mul R3 R3 3
sub R2 R3 R1
If the initial value of the registers is the following:
What is the final result of the registers after the program executes?
Answer
Assembly Instruction Encoding
Since EDSAC (1949), all computers store instructions in the same way that they store data. Each instruction is a number:
- Each opcode has a unique number associated with it. For example,
add
may arbitrarily be 53.
- Each register has a binary number associated with its number. For example, if there were sixteen registers, then each register could be enumerated from 0-15 in three bits.
Hence, the following commands are equal:
add r3 r2 r1
11011 011 010 001
= 11011011010001
Bits and Encoding
\(m\) bits can encode \(2^m\) different values.
\(n\) values can be encoded in \(\lceil \log_2(n) \rceil\) bits
Exercise
What is the max number of registers that can be designed by a machine given:
- 16 bit instructions
- Num opcodes: 100
- All instructions are of form
(reg1, reg2) -> reg3
Size of opcode: \(\lceil \log_2 (100) \rceil = 7\), 9 bits remain. Since there are three arguments, reg1
, reg2
, and reg3
, each register number must have 3 bits. Hence, there are \(2^3 = 8\) possible registers.
As a side note, x86 is 64-bit, which means that many bits are unused. This allows for many more opcodes in the future – which helps CISC goals.
Storage Architecture
- Immediate values are when you specify constants in instructions.
- Data can also be stored in registers, which are used to hold values in the middle of operations.
- DRAM is for longer-term storage, when data needs to be in your program memory but doesn't need to be used right now.
- Strange storage...
- Along the lines of the history of computers, people have come up with some weird storage schemas. But they failed. So we don't talk about them.
- Stored in memory because all instructions are in memory
- Useful for loading small constants
For example:
ptr++;
This in assembly code in LC2K is this:
add R2 R2 1
It is adding a constant value of 1 to the register.
However, in ARM ISA, each address points to a byte in memory, so this would actually increment R2
by 4 in ARM.
The size of the immediate value is usually determined by how many bits are left in the instruction format.
Register Storage
First came the accumulator, which was a single register architecture. Instructions were of the form add #5
. You do not need to specify the register operated on, because there is only one.
Example Architectures
ARM
- 16 registers
- 32 bits per register
- Has r15 program counter, which stores the address of the next instruction. It can be read or written. Helpful for making efficient non-sequential code calls in branching.
Intel x86
- 4 general-purpose registers (eax, ebx, ecx, edx)
- 32 bits per register