Veamos cómo nos manejamos con variables.
.data ; LD R1, A (R0)
A: .word 5 ; R1 = variable en dir. de A + 0
B: .word 8
.code
LD R1, A(R0)
.data ; SD R1, A (R0)
A: .word 0 ; RES = VALOR DE R1 EN VARIABLE EN DIR. DE A + 0
.code
DADDI R1, R0, 5
SD R1, A(R0)
Se pueden dar en situaciones que impiden a la siguiente instruccion que se ejecute en el ciclo que le corresponde.
Los queremos evitar a toda costa. Si tenemos atascos significa que una instruccion esta estancada esperando algo de una instruccion anterior.
Provocados por conflicto de recursos.
En el MIPS sucede cuando dos instrucciones intentan acceder a la etapa MEM simultáneamente.
![Imagen1](https://user-images.githubusercontent.com/55964635/141484332-582750ca-f2a6-489a-b82e-5ea5d366c52e.png) | Dos instrucciones listas para pasar a la etapa de memoria. Se produce un atasco estructural y solo pasa una de ellas ***Tiene prioridad la primera instruccion que entró en el cause*** |
Significa: Read After Write.
Por software | Con NOPS | Ordenando Sentencias |
```s .data NUM1: .word 15 .code DADDI R1, R0, 8 DADDI R2, R1, 10 LD R7, NUM1 (R0) HALT ``` | ```s .data NUM1: .word 15 .code DADDI R1, R0, 8 NOP NOP DADDI R2, R1, 10 LD R7, NUM1 (R0) HALT ``` | ```s .data NUM1: .word 15 .code DADDI R1, R0, 8 LD R7, NUM1 (R0) DADDI R2, R1, 10 HALT ``` |
POR HARDWARE Si ya tenemos los valores necesarios, podemos “adelantarlos”
En estos buffers se almacena los valores para que los puedan usar en las próximas instrucciones. De esta manera no hace falta esperar a las etapas MEM y WB para usar los valores!. Este adelantamiento de operando lo llamamos Forwarding. Los buffers no se pueden ver en el simulador. Una ventaja que tiene esta técnica es que nos permite postergar la “necesidad” de los operandos.
¿Cuando conviente tenerlo activado? Siempre es conveniente tenerlo activado (en la mayoria de los casos)
Se producen cuando:
(WAR)
o escritura (WAW)
Ejemplo 1 | Ejemplo 2 |
```s .data n1: .double 9.13 n2: .double 6.58 res1: .double 0.0 res2: .double 0.0 .code L.D F1, n1 (R0) ; F1 = n1 L.D F2, n2 (R0) ; F2 = n2 ADD.D F3, F2, F1 ; F3 = F2 + F1 MUL.D F4, F2, F1 ; F4 = F2 * F1 S.D F3, res1 (R0) ; Guarda la suma en res1 S.D F4, res2 (R0) ; Guarda la mult en res2 HALT ``` | ```s .data n1: .double 9.13 n2: .double 6.58 res1: .double 0.0 res2: .double 0.0 .code L.D F1, n1 (R0) ; F1 = n1 L.D F2, n2 (R0) ; F2 = n2 ADD.D F3, F2, F1 ; F3 = F2 + F1 MUL.D F2, F2, F1 ; F2 = F2 * F1 MUL.D F4, F2, F1 ; F4 = F2 * F1 S.D F3, res1 (R0) ; Guarda la suma en res1 S.D F4, res2 (R0) ; Guarda la mult en res2 HALT ``` |
Provocados al esperar la decisión de otra instrucción anterior (por ejemplo: si se realiza o no un salto).
Tenemos dos tipos de saltos
Definicion: Atasco de salto, es un salto que tuvimos que haber hecho y no lo hicimos. (Empezamos a ejecutar una instrucción que no debiamos ejecutar). El programa se da cuenta, corta la ejecución y abajo comienza la sentencia que tendria que haber ejecutado.
Ejemplo:
Ejemplo | Estadisticas |
```s .data B: .word 5 .code DADDI R1, R0, 1 LD R2, B (R0) LOOP: DSLL R1, R1, 1 ; Desplazo a la izquierda DADDI R2, R2, -1 ; Cant. de desplazamientos que faltan BNEZ R2, LOOP ; Si no es 0 salto a LOOP HALT ``` | ![image](https://user-images.githubusercontent.com/55964635/141319796-9e25a0de-8651-48ea-9627-92abc1ae44ac.png) |
Al igual que los atascos de dependencia de datos, tenemos diferentes técnicas para evitar los atascos por saltos.
La primera se denomina Branch Target Buffer que consiste en tener un flag que indica que si se debe saltar incondicionalmente o no dependiendo de qué hizo antes (es decir, predice).
Cada vez que ese flag/buffer se actualiza cuenta como un atasco de salto! Cada vez que le erramos a la predicción cuenta como atasco.
Misprediction (nuevo atasco): Es un atasco que me va a contabilizar cuando el BTB este equivocado
Ejemplo:
¿Cuando conviene activar el BTB?
Como solo fallamos al principio y al final de loop, conviene usarlo cuando tenemos bucles muy grandes. (sin condiciones de por medio)
Buenoooo | Maloooo |
```Pas for i:=1 to 100000000 do begin // Hace algo end; ``` Vamos a tener dos atascos al principio y dos atascos al final | ```Pas for i:=1 to 100000000 do begin if i es par then // Hace algo else // Hace otro algo end; ``` Vamos a tener dos atascos en cada interación (porque vamos a fallar la predicción) |
Es la otra técnica. Simplemente consiste en ejecutar SIEMPRE la siguiente instrucción a un salto
Programa | Definicion |
```s DADDI R2, R0, 3 LOOP: DADDI R2, R2, -1 BNEZ R2, LOOP HALT ``` Se terminaría el programa ya que se ejecuta el ***HALT*** en la primera interación! | - Buscamos ubicar instrucciones que ***no dependen del salto*** - De esta manera aprovechamos una instrucción y no tenemos atascos de salto! - Cómo último recurso usar sentencias NOP |
Ejemplo Mal | Delay Slot Activado |
```s .data cant: .word 8 datos: .word 1, 2, 3, 4, 5, 6, 7, 8 res: .word 0 .code DADD R1, R0, R0 ; Inicializa R1 = 0 LD R2, cant (R0) ; R2 = cant LOOP: LD R3, datos (R1) ; R3 = elemento de datos en la posición R1 DADDI R2, R2, -1 ; Resta 1 a la cantidad de elementos a procesar DSLL R3, R3, 1 ; Multiplica por dos el elemento actual SD R3, res (R1) ; Almacena el resultado en la tabla de resultados DADDI R1, R1, 8 ; Avanza a la siguiente posición BNEZ R2, LOOP ; Si quedan elementos sigo iterando NOP HALT ``` | ![image](https://user-images.githubusercontent.com/55964635/141335494-364c723f-0629-43df-9db7-8dc6ed41a694.png) |
Ejemplo Bien | Delay Slot Activado |
```s .data cant: .word 8 datos: .word 1, 2, 3, 4, 5, 6, 7, 8 res: .word 0 .code DADD R1, R0, R0 ; Inicializa R1 = 0 LD R2, cant (R0) ; R2 = ca LOOP: LD R3, datos (R1) ; R3 = elemento de datos en la posición R1 DADDI R2, R2, -1 ; Resta 1 a la cantidad de elementos a procesar DSLL R3, R3, 1 ; Multiplica por dos el elemento actual SD R3, res (R1) ; Almacena el resultado en la tabla de resultados BNEZ R2, LOOP ; Si quedan elementos sigo iterando DADDI R1, R1, 8 ; Avanza a la siguiente posición HALT ``` | ![image](https://user-images.githubusercontent.com/55964635/141336345-a413cef9-aa34-43b5-bc52-18657310112f.png) |
MIPS utiliza IEE 754 para números en punto flotante.
Contamos con 32 registros: desde F0 (simpre vale 0) hasta F31
Tiene un único tipo de dato que es el .double
NO todas las etapas tardan lo mismo.
Esta arquitectura nos permite tener múltiples instrucciones en la etapa EX
Podemos ejecutar múltiples instrucciones en menos tiempo!
No todo es color de rosas. Introduce los siguientes atascos:
IMPORTANTE
CONVENCIONES
Las subrutinas deben garantizar el guardado de los registros que correspondan. De esta manera, una subrutina podrá llamar a otra sabiendo que esta no modificará el valor de estos registros, para poder mantener esta garantía, es necesario guardar los registros en la pila. (PERO MIPS NO TIENE PILA 😧). Pero existe un registro que por convención todas las subrutinas usarán como puntero al tope de la pila. Y ese registro es………
El registro $sp
PUSH $t1 | POP $t1 |
```s daddi $sp, $sp, -8 ; "Subo" una celda de memoria sd $t1, 0 ($sp) ; Almaceno el dato ``` | ```s ld $t1, 0 ($sp) ; Extraigo el dato daddi $sp, $sp, 8 ; "Bajo" una celda de memoria ``` |
Como ejemplo | Como lo vamos a usar |
```s .data NUM1: .word 5 NUM2: .word 8 RES: .word 0 .code LD R1, NUM1 (R0) LD R2, NUM2 (R0) JAL SUMAR ; Llama a la subrutina SD R3, RES (R0) HALT SUMAR: DADD R3, R1, R2 JR R31 ; Retorna al punto donde fue llamado ``` | ```s .data NUM1: .word 5 NUM2: .word 8 RES: .word 0 .code LD $a0, NUM1 (R0) LD $a1, NUM2 (R0) JAL SUMAR ; Llama a la subrutina SD $v0, RES (R0) HALT SUMAR: DADD $v0, $a0, $a1 JR $ra ; Retorna al punto donde fue llamado ``` |
Existen dos “registros” (es decir, dos celdas de memoria comunes)
Como son celdas de memoria se leen y escriben con instrucciones de memoria: LD/L.D/LBU/SD/S.D….
IMPRIMIR UN STRING
IMPRIMIR UN NÚMERO
LIMPIAR LA PANTALLA
Imprimir Entero | Imprimir Double | Imprimir String |
```s .data CONTROL: .word 0x10000 DATA: .word 0x10008 NUM: .word 2 .code LD $s0, CONTROL ($0) LD $s1, DATA ($0) LD $t0, NUM ($0) SD $t0, 0 ($s1) ; CONTROL = 2 DADDI $t0, $0, 2 SD $t0, 0 ($s0) ; LIMPIA LA PANTALLA ;DADDI $t0, $0, 6 ;SD $t0, 0 ($s0) HALT ``` | ```s .data CONTROL: .word 0x10000 DATA: .word 0x10008 NUM: .double 19.5 .code LD $s0, CONTROL ($0) LD $s1, DATA ($0) L.D f1, NUM ($0) S.D f1, 0 ($s1) ; Control 3 DADDI $t0, $0, 3 SD $t0, 0 ($s0) ;DADDI $t0, $0, 6 ;SD $t0, 0 ($s0) HALT ``` | ```s .data CONTROL: .word 0x10000 DATA: .word 0x10008 TEXTO: .asciiz "Hola, Mundo!" .code LD $s0, CONTROL($0) LD $s1, DATA($0) DADDI $t0, $0, TEXTO SD $t0, 0($s1) DADDI $t0, $0, 4 SD $t0, 0($s0) HALT ;(1) $t0 = 4 -> función 4: salida de una cadena ASCII ;(2) CONTROL recibe 4 y produce la salida del mensaje ``` |
Pantalla de 50x50px
PINTAR UN PÍXEL
LIMPIAR LA PANTALLA
```s .data ;Imprimir un pixel CONTROL: .word 0x10000 DATA: .word 0x10008 PIXEL: .byte 0, 185, 135, 0, 23, 10, 0, 0 .code LD $s0, CONTROL ($0) ; $s0 = CONTROL LD $s1, DATA ($0) ; $s1 = DATA LD $t0, PIXEL ($0) SD $t0, 0 ($s1) ; Mando el dato a DATA DADDI $t0, $0, 5 SD $t0, 0 ($s0) ; CONTROL = 5 HALT ``` | ```s .data coorX: .byte 24 ; X coorY: .byte 24 ; Y color: .byte 255, 0, 255, 0 CONTROL: .word 0x10000 DATA: .word 0x10008 .code ld $s0, CONTROL ($zero) ; $s0 = dir de CONTROL ld $s1, DATA ($zero) ; $s1 = dir de DATA ; limpia la pantalla daddi $t0, $zero, 7 sd $t0, 0 ($s0) lbu $t0, coorX ($zero) sb $t0, 5 ($s1) lbu $t1, coorY ($zero) ; $t1 = valor de coordenada Y sb $t1, 4 ($s1) ; DATA + 4 recibe el valor de coordenada Y lwu $t2, color ($zero) ; $t2 = color sw $t2, 0 ($s1) ; Pongo color en DATA daddi $t0, $zero, 5 sd $t0, 0 ($s0) ; Pinta el píxel HALT ``` |
LEER UN NÚMERO (ENTERO O FLOTANTE)
LEER UN CARACTER
Leer Double | Leer Infinito |
```s .data ;leer desde teclado CONTROL: .word 0x10000 DATA: .word 0x10008 NUM: .double 0.0 CARACTER: .byte 0 .code LWU $s0, CONTROL ($zero) ; $s0 = CONTROL LWU $s1, DATA ($zero) ; $s1 = DATA DADDI $t0, $zero, 8 SD $t0, 0 ($s0) ; CONTROL = 8 L.D f1, 0 ($s1) ; Tomo número en f1 S.D f1, NUM ($zero) ; Guardo en variable DADDI $t1, $zero, 9 SD $t1, 0 ($s0) ; CONTROL = 9 LBU $t1, 0 ($s1) ; Tomo caracter en $t1 SB $t1, CARACTER ($zero) ; Guardo en variable HALT ``` | ```s .data CONTROL: .word 0x10000 DATA: .word 0x10008 CARACTER: .byte 0 .code LWU $s0, CONTROL($0) LWU $s1, DATA($0) DADDI $s4, $0, 13 ; 13 el ascii del enter pá LOOP: DADDI $t1, $0, 9 SD $t1,0 ($s0) ; CONTROL = 9 ; *PRESIONA UNA TECLA* ; LA CPU GUARDA EL CARACTER EN DATA LBU $t1,0 ($s1) ; COMPARO CON EL ENTER BEQ $t1, $s4, FIN ; GUARDO LA VARIABLE SB $t1, CARACTER($0) ;TOMO LA DIR DE CARACTER DADDI $s3, $0, CARACTER ; MANDO DIR DE CARACTER SD $s3, 0 ($s1) ; IMPRIMIR CARACTER DADDI $t1, $0, 4 SD $t1, 0 ($s0) ; CONTROL 4 J LOOP FIN: HALT ``` |
Las instrucciones se organizan en fases de manera que esto sea posible
Fases | Funcion |
![if](https://user-images.githubusercontent.com/55964635/138571830-e84e64ad-3720-4abf-96cb-dff21fa88120.png) | ```Instruction Fetch``` - Se accede a memoria por la instrucción - Se incrementa el PC (antes conocido como el IP) |
![ID](https://user-images.githubusercontent.com/55964635/138571834-3aceb35f-500d-47f2-b236-2b1dd4600a34.png) | ```Instruction Decoding``` - Se decodifica la instruccion - Se accede al banco de registros por los operandos. Se atasca si no están disponibles - Se calcula el valor del operando inmediato - Si es un salto, se calcula el destino y si se toma o no (requiere acceder al banco de reg) |
![EX](https://user-images.githubusercontent.com/55964635/138571858-3e9bb33b-1038-414a-915f-213a92fc7652.png) | ```Execution``` - Si es una instrucción de cómputo, se ejecuta en la ALU - Si es un acceso a memoria, se calcula la dirección efectiva - Si es un salto, se realiza (se modifica el registro PC) |
![ME](https://user-images.githubusercontent.com/55964635/138571868-6e02dbbd-f576-4d33-a165-c52618c1dd16.png) | ```Memory access``` - Si es un acceso a memoria, se lee/escribe el dato |
![WB](https://user-images.githubusercontent.com/55964635/138571875-ba381a67-ea8c-44f3-98d0-c1f5e69b9d75.png) | ```Write Back``` - Se almacena el resultado ***(si lo hay)*** en los registros |
.data
A: .word 1
B: .word 2
.code
ld r1, A(r0)
ld r2, B(r0)
sd r2, A(r0)
sd r1, B(r0)
halt
.data
NUM1: .word 5
NUM2: .word 8
RES: .word 0
.code
LD R1, NUM1 (R0)
LD R2, NUM2 (R0)
;MULTIPLICAMOS
DMUL R3, R1, R2
;ALMACENAMOS EL RESULTADO EN RES
SD R3, RES (R0)
HALT
Potencia ——–
.data
B: .word 5
.code
DADDI R1, R0, 1
LD R2, B (R0)
LOOP: DSLL R1, R1, 1 ; Desplazo a la izquierda
DADDI R2, R2, -1 ; Cant. de desplazamientos que faltan
BNEZ R2, LOOP ; Si no es 0 salto a LOOP
HALT
.data
cadena: .asciiz "Caza"
.code
; La pila comienza en el tope de la memoria de datos
DADDI $sp, $0, 0x400 ; bus 10 bits 🡪 2^10 = 1024 = 0x400
; Guarda como primer argumento para upcaseStr
; la dirección de cadena
DADDI $a0, $0, cadena
JAL upcaseStr
HALT
upcaseStr: DADDI $sp, $sp, -16 ; Reserva lugar en pila -> 2 x 8
SD $ra, 0 ($sp) ; Guarda en pila $ra
SD $s0, 8 ($sp) ; Guarda en pila $s0
DADD $s0, $a0, $zero ; Copia la dirección de inicio de la cadena
LOOP: LBU $a0, 0 ($s0) ; Toma car. actual
BEQ $a0, $zero, FIN ; Si es el fin, termina
JAL upcase
SB $v0, 0 ($s0) ; Guarda el caracter procesado en la cadena
DADDI $s0, $s0, 1 ; Avanza al siguiente caracter
J LOOP
; Recupera los datos salvados en la pila
FIN: LD $ra, 0 ($sp)
LD $s0, 8 ($sp)
DADDI $sp, $sp, 16
JR $ra
; Pasa un caracter a mayúscula.
; Parámetros: $a0 -> caracter
; Retorna $v0 -> caracter en mayúscula
; No se utiliza la pila porque no se usan registros que deban ser salvados
upcase: DADD $v0, $a0, $zero
SLTI $t0, $v0, 0x61 ; Compara con ‘a’ minúscula
BNEZ $t0, salir ; No es un caracter en minúscula
SLTI $t0, $v0, 0x7B ; Compara con el car sig a 'z' minúscula (z=7AH)
BEQZ $t0, salir ; No es un caracter en minúscula
DADDI $v0, $v0, -0x20 ; Pasa a minúscula
salir: JR $ra ; Retorna
.data
BASE: .word 2
EXPONENTE: .word 3
.code
LD $t0, BASE($0)
LD $t1, EXPONENTE($0)
DADD $t2, $t2 , $t0
LOOP: BEQZ $t1, TERMINO
dmul $t2, $t2, $t0
DADDI $t1, $t1, -1
J LOOP
TERMINO: HALT