Subido por Martí Melció

Apunts MIPS computer organisation

Anuncio
1. Introducció i Von Neumann
Von Neumann
Computador: Màquina que pot ser instruïda per dur a terme seqüències
d’operacions aritmètiques o lògiques automàticament mitjançant programació.
(General purpose, programable, automàtic, amb memòria/storage).
Arquitectura: (d’un computador) descriu els elements i les relacions entre ells.
Von Neumann, elements:
- Memòria central -> emmagatzema dades i programa/instruccions que executarà.
- Processador o Central Procesing Unit (CPU) -> executa les instruccions i realitza
càlculs.
- Dispositiu d’entrada -> un teclat o ratolí per introducir dades al sistema.
- Dispositiu de sortida -> una pantalla o impresora per mostrar els resultats de les operacions.
- Bus de dades -> connecta components del sistema, permet comunicació i intercanvi d’info.
MEMÒRIA:
Emmagatzema dades de programa i dades d’instruccions en la mateixa.
CPU:
Carrega instruccions (Fetch) // Interpreta instruccions (Decode)
Carrega dades // Processa dades // Escriu dades
CPU
- Elements de la CPU:
1. Control Unit -> supervisa i dirigeix les operacions de la CPU (execució d’instruccions + accés a memòria) //
indica a ALU quina operació fer → fetch and decode.
2. ALU (Arithmetic Logic Unit) -> realitza operacions (lògiques/aritmètiques), aporta la capacitat de càlcul: obté
2 entrades i genera 1 sortida.
3. Registres -> petites àrees de memòria dins la CPU que emmagatzemen dades temporalment durant el
processament.
4. Bus de dades -> connecta ALU amb registres i memòria (permet transferir dades).
- Operacions bàsiques:
Només és possible executar programa guardat a la memòria central.
CPU repeteix un cicle constantment:
1. Carrega instruccions de la memòria al registre (FETCH)
2. Interpreta les instruccions (DECODE -> quina és?)
3. Executa la instrucció
Sistema d’un computador
1. Software d’aplicació: escrit en HLL (high level language), per realitzar tasques específiques per l’usuari.
2. Software del sistema: conté un compilador que tradueix el codi HLL a codi de màquina i un sistema
operatiu que dona serveis al Software A i al Hardware (s’ocupa d’input/output, gestiona memòria i
emmagatzematge, programa tasques i comparteix recursos).
3. Hardware: composat per un processador, memòria i controladors d’input/output. És el component físic del
computador, realitza càlculs i executa el software.
Nivells de codi de programació:
1. HLL: nivell d'abstracció més proper al domini del problema i està dissenyat per ser fàcil de llegir i escriure
pels humans. Permeten als programadors treballar més productivament i fan que el codi sigui més portable
(més fàcil de moure d'un computador a una altre)
2. Llenguatge assemblador: representació textual de les instruccions que han de ser executades per la CPU.
De baix nivell, permet als programadors treballar directament amb la representació de hardware del
computador. → algoritme de traducció implementat per l'assemblador.
3. Binari / llenguatge de màquina: els bits són la representació de hardware de la informació a la computadora
i s'utilitzen per codificar instruccions i dades.
Components bàsics d'un ordinador:
1. Dispositius d’input/output: dispositius d'interacció amb l'usuari (monitor, teclat, ratolí...)
2. Dispositius d'emmagatzematge: disc dur, CD/DVD, unitat flash...
3. Adaptadors de xarxa: per comunicar-se amb altres ordinadors a través d'una xarxa.
Aquests components són els iguals per tots els ordinadors.
Dins el processador (CPU):
- Datapath: realitza operacions sobre les dades.
- Control: seqüència el datapath, la memoria, etc.
- Memòria caché: petita memòria SRAM ràpida per l’accés immediat a les dades.
Abstraccions:
Ajuden a gestionar la complexitat, amaguen els detalls de nivell inferior i proporcionen una interfície entre el
hardware i el software.
- L' Instruction Set Architecture (ISA) és la interfície (punt de connexió entre 2 sistemes) entre hardware i
software.
- Interfície binària d'aplicació: ISA + la interfície del software del sistema.
Llenguatge de programació de nivell alt:
- Intèrpret: tradueix una instrucció a la vegada, requereix menys temps per analitzar el codi font, però té un
temps d'execució més lent en general. No es genera cap objecte intermedi i tradueix el codi fins que es troba
un error. Els programes són més difícils de garantir que siguin correctes. (Python, Ruby, Java...)
- Compilador: escaneja tot el programa i el tradueix sencer en un altre llenguatge. Requereix més temps per
analitzar el codi font, però genera un objecte intermedi que ha de ser enllaçat. Només mostra errors després
d'analitzar tot el programa, el que resulta en programes més segurs.
(C, C++)
RISC i CISC:
CISC (Complex Instruction Set Computing) y RISC (Reduced Instruction Set Computing) són les dos
arquitectures principals utilitzades en la construcció de processadors.
- CISC: es centra en el maquinari i té múltiples mides i formats d'instruccions. Utilitza menys registres i té més
modes d'adreçament, però les instruccions poden trigar un temps variable, son difícils de pipeline.
- RISC: es centra en el programari i té instruccions d'un mateix conjunt amb pocs formats. Utilitza més registres
i té menys modes d'adreçament, però les instruccions triguen un sol cicle de temps i són fàcils de pipeline.
Té conjunts més petits i simplificats d'instruccions, les quals son uniformes (mides iguals) → descodificació i
adreçament més fàcils. Descodificats i executats directament a hardware. (MIPS, ARM, Alpha, PowerPC, RISCV...).
El compilador RISC fa una optimització del codi. Com que té menys instruccions, les tasques són més
optimitzades i hi ha menys errors.
*Pipeline: dividir el processament d’instruccions en diferents etapes, i es realitzen en paral·lel per fer +1 tasca alhora
MIPS (Microprocessor without Interlocked Pipeline Stages):
Arquitectura de processador dissenyada entre 1981-1985 per Hennessy i Patterson.
Les seves variants són utilitzades avui en dia en routers de Cisco i Linksys, modems ADSL, targetes
intel·ligents, controladors de impressores làser, descodificadors de TV, palmeres, Sony PlayStation 2 i Sony
PlayStation Portable.
MIPS32 (1986) és de 32 bits i inclou la seva ISA i la interfície del sistema de software. Treballa a freqüències
de 8.3 MHz, 12.5 MHz i 15 MHz i té 110.000 transistors.
2. Representació i aritmètica binària
Decimal:
→Binari :2 (dividim 2). Ex: 25/2=12 residu=1 12/2=6 residu=0 6/2=3 residu=0 3/2=1 residu=1 1/2=0 residu=1.
Quan no es pot fer més, agafem l’últim quocient i tots els residus en ordre invers: 11001.
En 8 bits: 00011001
→Hexadecimal :16 (dividim 16). Ex: 63/16=15 residu=3. Agafem l’últim quocient i tots els residus en ordre invers:
3F (mirem la taula)
Hexadecimal:
→Decimal: 160=1, 161=16, 162=256, 163=4096, 164=65536, 165=1048576... De dreta a esquerra.
Ex: FAB16 = 11×160 + 10×161 + 15×162 = 11 + 160 + 3840 = 4011
→Binari: Desagrupar 1 hexa i transformar-lo en 4 bits. Ex: 0x3BFD 3 B F D ↔ 0011 1011 1111 1101.
Binari:
→Decimal: 20=1, 21=2, 22=4, 23=8, 24=16, 25=32, 26=64, 27=128, 28=256, 29=512... De dreta a esquerra
Ex: 10100010 → 20×0 + 21×1 + 22×0 + 23×0 + 24×0 + 25×1 + 26×0 + 27×1 = 2 + 32 +128 = 162
→Hexadecimal: Agrupar 4 bits en un hexa. Ex: 0011 1011 1111 1101 ↔ 3 B F D
Nombres enters (positius i negatius)
Complement a 2: Es construeix substituint cada bit 1→0 o bé 0→1, després li sumem 1. Hem de tenir en compte
els carry i overflows.
Ex: −3710 -> Convertim (el valor absolut) a base 2: 1001012. Afegim leading 0’s 001001012. Neguem tots els bits. 110110102. Sumem 1:
110110112.
- Propietats del complement a 2
Regla d’overflow: sumem 2 nos del mateix signe, hi ha overflow si el resultat és de signe contrari.
Regla de la resta: (A – B) → (A + (−B)): representem B en complement a 2 i fem suma normal.
Unsigned: si hi ha carry al final de sumar-ho tot (si sumem 2 4-bit nº i el resultat es de 5 bits = INCORRECTE)
→ overflow (Rang [0, 2n - 1]).
Signed: si hi ha carry (l’eliminem)→ no hi ha overflow. (+)+(+) = – , (–)+(–)=+ →Overflow.
(Rang [-2n-1, 2n-1-1])
3. Coding strings and coding instructions
ASCII (American Standard Code for Infromation Interchange):
Cada símbol està representat per 1 byte. 0 a 127 estandars, 32 a 126 printable.
ISA (Instruction Set Architecture):
És l’especificació de les instruccions que pot entendre i executar el CPU. Defineix el conjunt d’instruccions que
un processador pot utilitzar per dur a terme operacions i càlculs. També defineix el format de les dades que el
processador pot manipular, així com els registres i les direccions de memòria usades pel processador. També
especifica els components del CPU visibles per al programador:
- el conjunt d'instruccions: aritmètiques, lògiques, salts, comparacions, moviments, accés a la memòria
- tipus de dades (mides, etc.)
- registres del CPU, la memòria
- controladors d'entrada i sortida (perifèrics)
- altres aspectes relacionats amb el funcionament del CPU
Una ISA és necessària perquè permet que els programes escrits per un ordinador es puguin executar en altres
ordinadors que implementen la mateixa ISA (els programadors no han de reescriure els seus programes per
cada ordinador diferent, i els fabricants d’ordinadors no han de crear un sistema operatiu i programari específic
per a cada model (així és més fàcil crear programari i fabricar ordinadors)). Normalment es veuen famílies de
màquines, totes amb la mateixa ISA, però amb característiques de potència, rendiment i cost diferents.
Instruction Set:
Per utilitzar el hardware, hem de parlar el seu idioma (instruccions = paraules, set d’instruccions = vocabulari)
Els dissenyadors de CPU tenen un objectiu comú: trobar un llenguatge que faciliti la construcció del maquinari
i del compilador, maximitzar l’output i minimitzar els costos i el consum d'energia. Mantenir la "simplicitat" és
tan valuosa per als ordinadors d'avui com ho va ser en el passat. (Principi de disseny 1: La simplicitat afavoreix
la regularitat. → La regularitat fa que la implementació sigui més senzilla. // La simplicitat permet un major
rendiment a un menor cost.)
MIPS:
- Arquitectura RISC: utilitza un conjunt reduït d'instruccions (simples i eficients), però amb gran capacitat de
paral·lelisme (alta eficiència i rendiment)
- Instruccions (format binari) de 32 bits: permet una gran eficiència en l'ús de la memòria.
- 6 bits = opcode (indica l'operació que s'ha de realitzar)
- 5 bits = números de registre d’input
- 5 bits = números de registre d’output
- 16 bits = dades addicionals (paràmetres / adreces de memòria depenen de l'opcode)
- Registres de 32 bits: permet una gran capacitat de càlcul.
- Suport per a aritmètica amb coma flotant: permet gran precisió en càlculs científics i tècnics.
- Amplament utilitzat: en sistemes embarcats, routers, computadors, dispositius de xarxa...
PC (Program Counter):
És un registre que gestiona les instruccions següents en la memòria. Com que el PC sempre apunta a la
"pròxima instrucció", aquesta direcció hauria de ser fàcilment actualitzable. Mètode habitual = sumar 4 a
l'adreça actual (instrucció = paraula de 32 bits i cada paraula de 32 bits té una direcció 4 vegades superior a
l'última).
És un registre especial en un ordinador que indica la ubicació de la pròxima instrucció a ser executada. El PC
conté l'adreça de memòria de la pròxima instrucció a ser llegida i executada pel processador. El PC
s'incrementa automàticament per cada instrucció executada, permetent així que el processador segueixi l'ordre
d'execució de les instruccions en un programa.
Registres de MIPS (tipus):
- PC = adreça de propera instrucció a ser executada
- $zero = sempre valor 0 (no modificable)
- $at = reservat per us intern de l’assemblador
- $v0-$v1 = per emmagatzemar resultats d’operacions + registres temporals intercanvi dades
- $a0-$a3 = arguments per funcions i procediments
- $t0-$t9 = registres temporals generals
- $s0-$s7 = registres de guardat
- $k0-$k1 = reservats per us del sistema.
- $gp: global pointer (apunta a la zona de memòria global)
- $sp: stack pointer (apunta al tope de la pila)
- $fp: frame pointer (apunta a l’inici del marc de pila actual)
- $ra: return address (adreça a la que retornarà el programa després d’una crida a una funció)
- La memòria està organitzada per bytes, cada byte ocupa una posició a l’adreça i s’accedeix al contingut
d’aquest byte a través de la seva adreça.
- Les words a MIPS estan organitzades en sets de 4-bytes i estan adreçades en 0,4,8,12, ...
La “main memory" s’utilitza per dades complexes (arrays, estructures, dinàmiques de dades...). Per aplicar
operacions aritmètiques carreguem els valors de la memòria en un registre i emmagatzemem el valor del
resultat del registre en la memòria. La memòria està dirigida per bytes (cada direcció identifica un byte). Les
paraules s’alineen a la memòria (la direcció ha de ser múltiple de 4).
La unitat de control MIPS descodifica els 6 bits a cada extrem dels camps de codi d'operació i el codi d'operació
de 32 bits per determinar cada seqüència d'instruccions.
Instruccions a MIPS (tipus):
MIPS operacions i operands:
Les instruccions de l’assemblador estan formades per un codi d’operació (instrucció -> que executar) +
operands (valors d’input/output per l’operació).
- Aritmètiques, lògiques, shifts, comparatives, load/store, branch/jump, operacions de control, ...
Assembly Language elements:
Directius (comencen amb “.”), Codi (variables, instruccions), Etiquetes (acaben amb “:”) i Comentaris (“#”)
.data -> indica el començament d’un segment de dades (declaració de variables + inicialització)
- Label -> identificador “:” cada label represente una adressa de memoria unica
- Data type -> descriu la mida en bits de les dades = .word (32) / .half (16) “ints”, .byte (8) “char”, .ascii(z)
“strings”, .double / .float “real”, .space (reserva nº consecutiu d’espais)
- Data value -> el valor ha de ser correcte acord am bel tipus de dada
.text -> indica el començament del segment de codi (codi del programa)
4. Tipus d’instruccions a MIPS
1. Moviments de Dades:
- la (load address): la target register, label ♯ target register ← address in memory by labels
- li (load immediate): li target register, label ♯ target register ← constant of 32-bits
- move (from one register to another): move target register, source register ♯ target register ← source register
S’utilitzen per moure dades entre registres i memòria → Load: llegir data de memòria al registre // Store:
escriure dades del registre a la memòria
- lw (load word): lw target register, constant(register)  target register ← Mem[constant + register]
- sw (store word): sw source register, constant(register)  Mem[constant + register] ← source register
***The register must previously contain a memory address. These instructions require that the calculated
memory addresses (Regsiter + Constant) to be multiples of 4. (constant is also called the offset)
lb, lbu, sb, lh, lhu, sh…
2. Aritmètiques: → 3 operands (rd, rs, rt)
- Suma (add) i resta (sub):
“add a, b, c” #a = b + c (detecten overflow)
“sub a, b, c” #a = b - c
“addi a, b, imm” #a = b + imm
“addu, addiu, subu ...” (no detecten overflow)
No hi ha instruccions per resta amb
immediates: “addi a, b, -imm”
- Divisió (div):
“div rs, rt” #dividend: rs divisor: rt (quotient stored in lo and remainder in hi)
“divu rs, rt” #dividend: rs divisor: rt
- Multiplicació (mult):
“mult rs, rt” (lower part of the result stored in lo and the higher in hi)
“multu rs, rt” (lower part of the result stored in lo and the higher in hi)
“mflo rd” (rd  lo)
“mfhi rd” (rd  hi)
“mul rd, rs, rt” (rd  rs * rt)
Registres vs Memòria:
Registres = +ràpids d’accedir. Operar amb dades a la memoria requereix loads i stores. Optimització: el
compilador ha d’utilitzar els registres per emmagatzemar les variables del programa (al màxim).
3. Lògiques: (avalua bit per bit)
- AND: produeix 1 si tots dos bits d’origen són 1
“and rd, rs, rt” // “andi rt, rs, imm”
➔ Per saber si un registre és parell o no, farem un AND amb 0001. Si el resultat té un 1 a l'últim bit, vol dir
que el registre és senar (tots els nºs binaris parells acaben en 0, i els múltiples de 4 amb dos zeros).
→ Per “mask” bits a una word per seleccionar alguns bits i eliminar la resta (convertint-los en 0)
- OR: produeix 1 si almenys 1 bit d’origen o tots dos són 1.
“or rd, rs, rt” // “ori rd, rs, imm”
→ Per activar (set to 1) bits específics d’una word, deixant la resta sense canvis.
- NOT: inverteix els bits.
- NOR (not + or): produeix un 1 quan tots dos bits d’origen són 0. Per invertir bits en una word.
“nor rd, rs, rt”
- XOR (exclusive or):
“xor rd, rs, rt”
4. Jumps i comparacions:
- Comparacions: per decidir quina part del codi s'ha d'executar a continuació. Després d'una instrucció de
comparació, és necessària una instrucció per indicar on ha de continuar el programa (jump instructions)
- Jumps: Salta per executar una part diferent del programa, no la següent instrucció (s’indica untilitzant una
label). (sense condicions). L'adreça de la instrucció a la qual salta no és necessàriament propera a l'adreça
actual en el PC. Per a l'abstracció procedural: crides de subrutina.
- Branch: compara el valor dels operands segons diferents condicions. Si la comparació és certa, salta per
executar la instrucció donada, sinó, segueix executant la següent instrucció. L'adreça de la instrucció a la
qual saltar és propera a l'adreça actual en el PC (salt relatiu del PC). Per a instruccions de flux de control
(loop, if-then-else).
Bit shifting:
Els bits moguts fora (al final/principi) son descartats.
Un shift a l’esquerra d’n bits pot ser equivalent a multiplicar per 2n. (S’insereixen zeros
a la dreta, no es pot utilitzar per dividir números entre 2 si son de 2’s compliment).
Un shift aritmètic a la dreta d’n bits equival a dividir per 2n (sra). (el most sign bit
s’insereix a l’esquerra, conservant el signe).
sra rd, rt, shamt
srav rd, rt, rs
sll rd, rt, shamt
sllv rd, rt, rs
srl rd, rt, shamt
srlv rd, rt, rs
5. Vectors i arrays:
Arrays/Vectors: grup d’elements del mateix tipus en 1 dimensió. (organitzem les dades per aplicacions)
Matrius: grup d’elements del mateix tipus de múltiples dimensions.
Tots els elements (es poden accedir amb índex) es guarden en adreces de memòria consecutives.
Hi ha dues operacions bàsiques per realitzar utilitzant un vector (o matriu):
- Recuperar (obtenir) les dades emmagatzemades en una posició específica (llegir): x = llista[i]
- Emmagatzemar les dades en una posició específica (escriure) llista[i]=x
Per accedir als elements d'un vector, és necessari calcular l'adreça de memòria en què es troba l'element
(@V[i] = @V + [i * element size in bytes]) i utilitzar les instruccions load i store per accedir al contingut.
Com accedir a un valor? 1. Accedir a l’adreça base, 2. Calcular la ubicació i load les dades per les operacions
Alignment:
MIPS requereix que les dades estiguin alineades.
.align n alinea la seguent dada en una adreça multiple de 2n, .half (2) .word (4) s’alinean automatic.
.align 0 desactiva l’alineament automatic.
Syscall:
Crides al sistema operatiu per realitzar una acció específica. Ho utilitzarem per imprimir dades en la consola /
llegir dades de la consola. Per utilitzar syscall: El codi del sistema (instrucció) es carrega en el registre $v0.
Els arguments, si són necessaris, es carreguen en els registres $a0, $a1... Executar (invocar) syscall.
Retorna valors, si existeixen, en els registres.
Decision making:
6. Pointers
* apunta memòria (32 bits) -> on comença (* dona contingut // & dona adreça)
- Als punters se’ls hi assignen 4 bytes independentment del tipus de dades
&V[0] ≡ V ≡ &V <- adreça de memòria.
(*a)++ != *a++
Pointer Arithmetic:
El tipus de punter indica com
actualitzar l’adreça (en C)
Matrius:
És possible un ordre no consecutiu.
Quan s'emmagatzema de forma no consecutiva, la matriu es pot emmagatzemar com
un vector de punters a les files. Cada apuntador indica on es troba un vector.
int matriu [ i ][ j ] → a MIPS, si és una variable global, es declara:
.data
matriu: .space NR x NC x 4
.space ha d'estar alineat segons el tipus de dades que conté la matriu
Accés aleatori a un element d'una matriu: &matrix[ i ][ j ] = &matrix + (i * NC + j) * size
Accés seqüencial: s'utilitza per examinar/recuperar algun o tots els elements d'un vector o matriu que estan
separats per una distància constant.
- Si no hi ha un patró, no es pot fer un accés seqüencial
- L'accés als elements es fa a través d'un punter
- L’adreça de memòria que ocupa cada element es calcula a partir de l'adreça de l'element anterior
accedit en l'scan (no a partir de l'aplicació de la fórmula general)
- La forma general de l'algorisme és la següent:
* Inicialitza un punter amb l'adreça de memòria del primer element de l’scan
* Itera mentre l’scan no acabi
* Accedeix a l'element a través del punter.
* Actualitza el punter amb l'adreça de memòria del següent element a accedir en l’scan
7. Subrutines
Abstracció procedural: (el primer tipus d’abstracció)
- Factorització: extreure lo comú, eliminar lo repetitiu i extreure'n l'essència
* perquè només s'escrigui una vegada
* perquè sigui genèric
* perquè puguem tenir confiança en la seva exactitud
Un conjunt d'instruccions que executen una tasca específica que es pot cridar des de qualsevol part del
programa, incloent ell mateix. Un codi amb subrutines és més estructurat, és molt més ràpid programar
utilitzant subrutines, però hi ha més sobrecàrrega (instruccions addicionals per gestionar la crida (i la
finalització) de subrutines).
(ABI) Convenció de crida: l'autor de la funció i l'autor del codi que la invoca han de posar-se d'acord en els
detalls (com invocar-la, quins són els paràmetres, valor de retorn, ús de registres). [ABI de MIPS]
Passos a seguir:
1. Preparar els paràmetres → registres: $a0, $a1, $a2 i $a3 (en ordre)
2. Cridar la subrutina i desar el punt de retorn → jal
3. Desar l'estat o context
4. Desar les variables locals
5. Executar el codi de la subrutina
6. Si hi ha un valor de retorn (resultat), posar-lo en el lloc apropiat → $v0
7. Eliminar/descartar totes les variables locals (destruir el context local)
8. Restaurar l'estat i context que existia abans de la crida
9. Retornar el control al que crida (usant l’adreça de retorn) → jr $ra
10. Recollir el valor retornat (si hi ha) → $v0
11. Tornar al punt de retorn
El programa principal no utilitza
registres temporals.
Seria catastròfic si el programa
principal utilitzés $t0 o $t1 que
són utilitzats en la subrutina.
Crida: jal subrutina → copia l'adreça de retorn al registre $ra, salta a l’adreça indicada per l'etiqueta (tipus J)
Retorn: jr $ra → actualitza el PC (pc ← $ra)
Paràmetres: hi ha dues maneres típiques de transmetre’ls
- Per valor: s'envia una còpia de la variable original, el receptor veu la còpia com a variable local
- Per referència: s'envia l'adreça de memòria de la variable original, el receptor pot modificar el valor
A MIPS, les variables de tipus de dades bàsiques es poden enviar per valor o per referència i les variables
estructurades s'envien per referència
Enviar paràmetres: si les dades a enviar són un caràcter, un nombre natural, un enter o un punter, s’envien
col·locant-les als registres $a0, $a1, $a2 i $a3 (en ordre). Si el resultat és un caràcter, un nombre natural, un
enter o un punter, són col·locats en el registre $v0
Els registres $t0–$t9, $a0–$a3, $v0–$v1, $f0–$f19 la subrutina pot modificar-los
Els registres $ra, $sp, $s0–$s7 no s’han de modificar, la subrutina els preserva
La subrutina pot utilitzar qualsevol registre, però si vol utilitzar un que no sigui $t0–$t9, $a0–$a3, $v0–$v1 o
$f0–$f19, ha de guardar primer el seu valor, i per això ha d'utilitzar la pila (stack)
Pila → tipus de dades abstracte que serveix com a col·lecció d'elements, amb tres operacions principals:
- Empènyer (push), que afegeix un element a la col·lecció
- Treure (pop), que treu l'element afegit més recentment
- Comprovar si està buida, que retorna verdader si la pila no té elements
En MIPS, l'ús de la pila és per a fins generals. No està limitada a la gestió de subrutines, sinó que es pot utilitzar
lliurement. El punter de la pila és proporcionat per $sp29 → Inicialment $ sp= 0x7FFFFFFC (en múltiples de 4).
La pila creix cap a adreces decreixents.
Desar i restaurar el context a la pila: s'han de fer dues coses (principi i final de la subrutina):
- Desar: reservar espai a la pila (reduint $sp en múltiples de 4) i guardar els valors de $s0–$s7, $ra que
es faran servi
- Restaurar: restaurar els valors dels registres modificats traient-los de la pila i restaurar el valor de $sp
perquè es mantingui sense canvis
Bloc d'activació: cada subrutina crea al principi el seu bloc d'activació i l’ha de destruir abans de finalitzar i
assegurar-se de restaurar els registres
- reservar espai a la pila per a tot el bloc amb una sola instrucció → addiu $sp, $sp, -nbytes
- ficar a la pila els registres que s'han de desar
- reservar espai per a les variables locals
Subrutina que crida a una altre subrutina
No podem reutilitzar el registre $ra. Hem de posar a la pila els valors del registre $ra i recuperar-los de la pila
quan acabem una subrutina. (s’ha de guardar $ra)
Una subrutina ha de desar $ra quan la subrutina crida una altra subrutina. Ha d’utilitzar $s quan necessita
utilitzar un valor d’un paràmetre o variable local després de la crida de l’altre subrutina o quan hi ha moltes
crides a subrutines i és necessari recollir el resultat d'una d'elles en $v0, abans que una altra la reutilitzi.
Aplicacions de la pila:
-
Implementar funcions recursives
Convertir expressions in-fix a post-fix
Avaluar expressions post-fix
Stack (pila): és una estructura de dades molt útil i és complementària a una cua.
Pila:
Cua:
- PUSH(element): emmagatzema element a pila
- element ← POP(): recupera l'element de la pila
- IS EMPTY(): verdader només si la pila és buida
LIFO: l'últim que entra, és el primer que surt
- ENQUEUE(element): emmagatzema element a cua
- element ← DEQUEUE(): recupera l'element de la cua
- IS EMPTY(): verdader només si la cua és buida
- FIFO: el primer que entra, és el primer que surt
Subrutines: Variables locals:
- variables globals es guarden a la memòria principal (.data)
- variables locals es guarden en registres o a la pila
Si es necessiten massa variables i no hi ha prou registres: s'emmagatzemen a la pila
Quan es representen les variables locals a la pila, es respecta l’alienació i es guarden en el mateix ordre en
què es declaren al llenguatge de programació d'alt nivell.
Variables locals: scope dinàmic → comencen a existir en invocar-se la subrutina i desapareixen al quan acaba
RESUMET:
Una subrutina no és responsable de preservar: $t0–$t9, $a0–$a3 i $v0–$v1, però si de: $ra, $sp i $s0–$s7
La implementació ha de:
- Utilitzar els registres $sx si volem preservar valors més enllà de les crides a la subrutina
* Una variable local (char, short, int) ha de ser implementada en un registre $s si li assignem un
valor abans de cridar una funció i volem conèixer aquest valor després d’invocar-la
* Per a un paràmetre que hem rebut, si ens interessa el seu valor després de qualsevol altra
crida, hem de moure'l de $ax a $sx.
- Utilitzar els minims registres $sx possibles, per minimitzar la seva emmagatzemació i restauració
- Verificar sempre si s'utilitza un registre $sx, guardar-lo i restaurar-lo
Inorder Tree: (in-fix)
Postorder Tree: (post-fix)
8. Cache
Una petita quantitat de memòria d'accés ràpid que emmagatzema temporalment les dades més utilitzades per
un processador per accelerar l'accés a aquestes
Hit: Hi ha una referència a una ubicació de memòria i aquesta
es troba en el nivell superior
Hit Rate: % d'accés a memòria que es troben en el nivell
superior (𝑓ℎ𝑖𝑡 ) → 𝐻ⅈ𝑡 𝑅𝑎𝑡𝑒 =
𝑛𝑜 𝑜𝑓 𝐶𝑎𝑐ℎ𝑒 𝐻𝑖𝑡
𝑛𝑜 𝑜𝑓 𝑀𝑒𝑚𝑜𝑟𝑦 𝐴𝑐𝑐𝑒𝑠𝑠𝑒𝑠
Miss: Accés a una ubicació de memòria i aquesta
ubicació no es troba en el nivell superior. Fallada de cau,
fallada de memòria.
Miss Rate: % d'accés a memòria que no es troben en el
nivell superior (𝑓𝑚𝑖𝑠𝑠 ) → 𝑀ⅈ𝑠𝑠 𝑅𝑎𝑡𝑒 = 1 − 𝐻ⅈ𝑡 𝑅𝑎𝑡𝑒
Descargar