Codificación de Instrucciones
Ciencias de la Computación III
Diego José Figueroa
Programa almacenado
Programa almacenado
- Las instrucciones se tratan igual que los datos.
-
Las instrucciones se representan como secuencias de bits.
-
Leemos y cargamos programas completos, como si fueran datos.
-
¿Consecuencias?
Direcciones
- Para que esto funcione, necesitamos una forma de ubicar una instrucción en memoria.
- A cada instrucción se le asigna una dirección.
- Program Counter ($pc)
- IAC (Instruction Address Counter) para Intel.
RISC
- Optamos por un diseño simple
- Pocas instrucciones
- Muchos registros
- Operaciones sencillas
- Codificación de instrucciones simple
- Las instrucciones se codifican más o menos igual.
- Utilizan un tamaño fijo
- En general, bastante sencillo.
- Lo contrario de CISC
¿Por qué muchos registros?
- Ventajoso para los compiladores
- Permite implementar optimizaciones complejas.
- Cálculos intermedios.
- Valores temporales.
- Mejorar la calidad del programa.
- Ventajoso para los programadores/diseñadores
- Permite realizar más cálculos sin recurrir a memoria.
- Computadoras con mejor rendimiento.
Más razones...
- ILP (Instruction Level Parallelism)
- Si dos instrucciones no utilizan los mismos registros, se pueden ejecutar en paralelo.
- Con más registros, es más fácil para el compilador lograr esto.
- Con más registros, la probabilidad de repetir registros disminuye.
¿Por qué preferimos
instrucciones simples?
Instrucciones Simples
- Hacer poco, pero hacerlo bien.
- Pocas instrucciones, pero que se ejecuten muy rápido.
- Escogemos instrucciones que permitan hacer composiciones.
- Más fácil si tenemos muchos registros.
- La mayor parte del assembler lo escribe un compilador
- Los compiladores usan instrucciones simples mayormente.
- Instrucciones complejas no las aprovechan.
Más razones...
- El hardware es difícil y costoso de diseñar
- Mientras más complejo es el diseño
- se complica la construcción
- se incrementan los costos
- no necesariamente se mejora el desempeño
add $s0 $s0 $0

¿Por qué codificar?
- El procesador NO entiende assembler.
- El assembler es una abstracción, facilita pasar de alto nivel a bajo nivel.
- Es necesario traducirlo a algo que el procesador pueda manejar.
El procesador sólo entiende 0s y 1s
¿Cómo codificar?
- Queremos un sistema simple
- Tamaño fijo
- Todas las instrucciones ocupen la misma cantidad de bits.
- 32 bits es un buen número.
- Formato de 3 registros
- 1 destino, y 2 operandos
¿Cómo codificar?
- Además, debe ser fácil de decodificar
- Las instrucciones se deben codificar de forma parecida.
- A esto se le llama formato.
- Dividir la codificación en campos
- Una parte para el destino, otra para los operandos, etc.
- Suficiente espacio para valores inmediatos
- 16 bits es un buen tamaño
- Ej: offsets para los corrimientos, lecturas/escrituras de memoria.
Codificación de
instrucciones de MIPS
3 formatos
Cosas en común
- Los 3 formatos ocupan 32 bits.
- Los primeros 6 bits indican la operación:
- Además, nos indican qué formato utilizar
- Quedan códigos libres, para agregar instrucciones.
Tipo J
- 6 bits para la operación (opcode)
- 26 bits para la dirección destino
- Estas instrucciones deben ( y están) alinearse en fronteras de 4 bytes (word aligned).
- ¿26 bits? ¿direcciones de 32 bits?
- bits 28 - 31 se copian de $PC (MSB)
- bits 0 - 1 son siempre 0.
¿Para qué se usa Tipo J?
- Saltos incondicionales
- No hay espacio para registros
- Por lo que no nos sirve para branches.
- Pero sirve bien para saltos incondicionales.
- Ejemplo:
- j label
- jal label
Tipo R
- 6 bits para la operación (0 para Tipo R)
- 5 bits para el 1er registro fuente (rs)
- 5 bits para el 2do registro fuente (rt)
- 5 bits para el registro destino (rd)
- 5 bits para la cantidad de bits a correr (shift amount)
- 6 bits que identifica la función a ejecutar
Tipo R
- El opcode SIEMPRE es 0, (00000).
- El primer registro fuente es un valor entre 0 y 31.
- Tenemos 32 registros, lo justo con 5 bits.
- El 2do registro fuente también ocupa 5 bits.
- Lo mismo para el registro destino.
- Número de bits a correr (shamt)
- Utilizado en instrucciones de corrimiento.
- 0 en cualquier otro caso.
- 6 bits para indicar la función
- Podemos tener hasta 64 funciones
- La suma por ejemplo, es el func-code 32.
Tipo R
- El func-code nos permite tener hasta 127 instrucciones
- 63 indicadas por el opcode
- 64 indicadas por el func-code (todas Tipo R)
- shamt, 5 bits son suficientes
- Nunca corremos más de 32 bits.
- Sólo se usa para corrimientos constantes.
- Corrimientos variables, se determina en base a un registro.
Ejemplo
add $t0 $t1 $t2
opcode = 0
rd = 8
rs = 9
rt = 10
shamt = 0
func-code = 32
Tipo I
- 6 bits para opcode
- 5 bits para registro fuente (rs)
- 5 bits para registro destino (rt)
- 16 bits para una constante
- Puede ser una dirección o un valor inmediato.
Tipo I
- Utilizado para instrucciones con valores inmediatos
- add $rt $rs imm
- También para branches
- beq $rt $rs label
- También para accesos a memoria
- lw $rt offset($rs)
Branches
- Utiliza un inmediato de 16 bits en notación con signo.
- Si no se cumple la condición,
- $PC = $PC + 4
- Si se cumple la condición,
- $PC = $PC + 4 + imm << 2
- Salto relativo a la instrucción siguiente
- Imm indica la cantidad de instrucciones a saltar
- hacia arriba, o hacia abajo.
Ejemplo
$addi $t0 $t1 -50
opcode = 8
rt = 9
rs = 8
imm = -50
Más ejemplos...
addi $12 $10 0xf3d
001000 01010 01110 0000 111100111101
add $3 $4 $5
000000 00100 00101 00011 00000 10000
jal foo (foo = 0xf0f0f0)
000011 0000 1111 0000 1111 0000 1111 00
Decodificación
- ¿Cómo pasamos de 0s y 1s a ensamblador?
- Primero:
- Identificar el formato en base al opcode.
- Utilizar el formato adecuado, para identificar los campos.
- Escribir la instrucción de MIPS correspondiente.
Ejemplo
[0x00400000] 0x00001025[0x00400004] 0x0005402A[0x00400008] 0x11000003[0x0040000C] 0x00441020[0x00400010] 0x20A5FFFF[0x00400014] 0x08100001
Ejemplo
000000000000000000010000001001010000000000000101010000000010101000010001000000000000000000000011000000000100010000010000001000000010000010100101111111111111111100001000000100000000000000000001
Ejemplo
Identificar el tipo
000000000000000000010000001001010000000000000101010000000010101000010001000000000000000000000011000000000100010000010000001000000010000010100101111111111111111100001000000100000000000000000001
Ejemplo
R 000000 00000 00000 00010 00000 100101
R 000000 00000 00101 01000 00000101010
J 000100 01000 00000 0000000000000011
R 000000 00010 00100 00010 00000 100000
I 001000 00101 00101 1111111111111111
J 000010 00000100000000000000000001
Ejemplo
Ejemplo
[0x00400000] or $2, $0, $0
[0x00400004] slt $8, $0, $5
[0x00400008] beq $8, $0, 3
[0x0040000C] add $2, $2, $4
[0x00400010] addi $5, $5, -1
[0x00400014] j 0x00400004
Ejemplo
or $v0, $0, $0
loop:
slt $t0, $0, $5
beq $t0, $0, exit
add $v0, $v0, $a0
addi $a1, $a1, -1
j loop
exit:
TAL vs MAL
- True Assembly Language
- MIPS Assembly Language
- MAL incluye pseudo-instrucciones
- move
- la
- ror
- mul
- ...
- Sólo instrucciones de TAL tienen una traducción a binario.
TAL vs MAL
- la $rd label
- Si el valor de label cabe en 16 bits
- addi $rd $0 *label
- Si el valor de label NO cabe en 16 bits
- lui $rd <parte_alta_de_label>
- ori $rd <parte_baja_de_label>
TAL vs MAL
- ¿Cómo identifica el ensamblador una pseudo instrucción?
- Lista de instrucciones no existentes (move, ror, etc.)
- Casos especiales (operador incorrecto, etc.)
- ¿Cómo convierte la instrucción a MAL?
- Reemplaza por una composición de instrucciones reales.
- Utiliza $at como registro temporal de ser necesario.
TAL vs MAL
addi $t0, $t1, 40000
beq $s0, 10, exit
sub $t0, $t1, 1
¿Cuáles son reales?
TAL vs MAL
addi $t0, $t1, 40000
beq $s0, 10, exit
sub $t0, $t1, 1
TAL vs MAL
addi $t0, $t1, 40000
beq $s0, 10, exit
sub $t0, $t1, 1
TAL vs MAL
addi $t0, $t1, 40000
beq $s0, 10, exit
sub $t0, $t1, 1
Title
12 - Codificación de Instrucciones
By Diego Figueroa
12 - Codificación de Instrucciones
- 4,475