Laboratori 1: Introducció al simulador RARS

Lliuraments

Cal lliurar les respostes als exercicis següents al campus virtual abans de l’inici de la sessió de laboratori:

Exercici Nom i format del fitxer
Exercici 28.1 s1_2_1.md
Exercici 28.2 s1_2_2.md
Exercici 28.3 s1_3_1.md
Exercici 28.4 s1_3_2.md
Exercici 28.5 s1_4_1.md
Exercici 28.6 s1_5_1.s i s1_5_1.md
Exercici 28.7 s1_5_2.s i s1_5_2.md

Lectura prèvia

Per fer la sessió, cal haver instal·lat RARS tal com s’indica a la Presentació del curs.

També cal haver llegit i entès els continguts següents:

Concepte On trobar-ho
Format del codi assemblador EC 2.7
Esquelet d’un programa RISC-V 2.19
Accessos a memòria (lw), desplaçaments (slli) i salts condicionals (bge, blt) Secció 2.6.3.1, Secció 3.1.1, Secció 3.4.1
Pseudoinstruccions la i li, i l’expansió de la en auipc + addi RISC-V 2.17, RISC-V 2.16, RISC-V 2.14
Essencial 28.1: Format del codi i esquelet

Es recomana seguir el format del codi (EC 2.7) i l’esquelet (RISC-V 2.19) en tots els programes lliurats al laboratori i a l’examen de laboratori, com fan els exemples. El format no s’avalua per si mateix.

28.1 El primer programa

El Codi 28.1 és un programa mínim executable a RARS: imprimeix un missatge per consola i en finalitza l’execució.

Codi 28.1: Programa mínim: escriu un missatge i acaba.
hola.s
.data
msg:    .asciz  "Hola, món!\n"

.text
        la      a0, msg         # a0 = adreça de msg
        li      a7, 4           # syscall 4: print_string
        ecall

        li      a7, 93          # syscall 93: exit2
        li      a0, 0           # codi de sortida: 0
        ecall

Per executar-lo a RARS:

  1. Copieu el codi a l’editor de RARS i guardeu-lo com hola.s.
  2. Assembleu amb Run → Assemble (o F3). Si hi ha errors, RARS els mostra al panell de missatges amb el número de línia.
  3. Executeu amb Run → Go (o F5). El missatge ha d’aparèixer al panell de consola.
Essencial 28.2: Crides al sistema de RARS

Una crida al sistema és una petició de servei al sistema operatiu (o, en el cas de RARS, al simulador). Es fa carregant el codi de servei a a7 i els paràmetres a a0–a2, i executant ecall. Les crides al sistema s’estudien en detall a Secció 9.5; per ara n’hi ha prou amb usar-les com a patró:

Codi de servei (a7) Servei Paràmetre (a0) Retorn (a0)
1 print_int enter a imprimir —
4 print_string adreça de la cadena —
93 exit2 codi de sortida —

28.2 Exploració del simulador

La secció treballa sobre el programa suma.s, que calcula la suma dels elements d’un vector d’enters i deixa el resultat al registre t0.

suma.s
.data
V:      .word   -1, -2, -3, -4, -5, -6, -7, -8, -9, -10

.text
        li      t0, 0           # t0 = suma = 0
        li      t1, 0           # t1 = i = 0
        li      t2, 10          # t2 = N = 10
        la      t3, V           # t3 = adreça de V

bucle:
        bge     t1, t2, fi      # si i >= N, surt
        slli    t4, t1, 2       # t4 = 4*i
        add     t4, t3, t4      # t4 = @V[i]
        lw      t4, 0(t4)       # t4 = V[i]
        add     t0, t0, t4      # suma += V[i]
        addi    t1, t1, 1       # i++
        j       bucle

fi:
        li      a7, 93
        li      a0, 0
        ecall

Un cop assemblat el programa (F3), la pestanya Execute mostra els panells que s’usaran durant la simulació:

  • Panell de codi desassemblat (Text Segment, esquerra): mostra les instruccions en memòria, una per fila, amb l’adreça, la codificació en hexadecimal i el mnemònic. La instrucció que s’executarà a continuació apareix ressaltada.
  • Panell de registres (Registers, dreta): mostra el contingut dels 32 registres de propòsit general i el PC. Els registres modificats a l’últim pas apareixen ressaltats.
  • Panell de memòria de dades (Data Segment, centre-baix): mostra el contingut del segment .data en format de paraules hexadecimals.
  • Panell de consola (Run I/O, baix): mostra la sortida de les crides al sistema i permet introduir dades.

Exercici 28.1 (s1_2_1.md) Assembleu suma.s i responeu les preguntes següents:

  1. On es mostra el valor del comptador de programa (program counter, PC)?
  2. RARS no mostra el nombre d’instruccions executades als panells principals. Quina eina de RARS permet comptar-les? Indiqueu el camí de menús per obrir-la i el pas necessari perquè comenci a comptar.
  3. El panell de codi mostra, per a cada instrucció, tres columnes de valors (a més de la casella d’activació de punt de ruptura i la columna de codi font). Quina informació conté cadascuna?

Solució 28.1.

  1. Al panell de registres (Registers), a la fila pc.
  2. Tools → Instruction Counter. Cal obrir-la abans d’executar el programa i prémer Connect to Program perquè comenci a comptar; si s’executa el programa abans de connectar l’eina, el recompte no es registrarà.
  3. Address (adreça de memòria de la instrucció), Code (codificació de la instrucció en hexadecimal) i Basic (la instrucció real, amb els registres identificats amb x0–x31).

Exercici 28.2 (s1_2_2.md) Algunes pseudoinstruccions s’expandeixen en més d’una instrucció real. Treballeu sobre suma.s ja assemblat.

  1. Localitzeu la pseudoinstrucció la t3, V al panell de codi desassemblat. Quines instruccions reals ha generat? Copieu-les exactament tal com apareixen al panell.
  2. Per quina raó la necessita dues instruccions reals en lloc d’una?
  3. La pseudoinstrucció li t2, 10, en canvi, genera una sola instrucció. Per quina raó en aquest cas n’hi ha prou amb una? Hauria passat el mateix amb li t2, 100000?

Indicació: els immediats de les instruccions tipus I tenen 12 bits amb signe (rang −2048..2047).

Solució 28.2.

  1. El panell mostra auipc x28,0x0000fc10 seguida d’addi x28,x28,0xfffffff4, és a dir, auipc t3, 0xfc10 i addi t3, t3, -12 amb els noms ABI (els valors exactes depenen de l’adreça de V i de la posició de la al programa; l’estructura auipc+addi és sempre la mateixa).
  2. Perquè una adreça de memòria ocupa 32 bits, i cap instrucció tipus I té un immediat de 32 bits: auipc aporta els 20 bits alts (relatius al PC) i addi hi suma els 12 bits baixos amb signe.
  3. 10 cap dins el rang [−2048, 2047] d’un immediat de 12 bits amb signe, per tant, una sola addi n’hi ha prou. Amb li t2, 100000 no passaria el mateix: 100000 no cap en 12 bits, de manera que RARS generaria lui t2, 0x18 seguida d’addi t2, t2, 1696 (dues instruccions), igual que amb la.

28.3 Execució i inspecció

RARS ofereix tres modes d’execució:

  • Execució contínua (F5): executa el programa fins al final (o fins al primer punt de ruptura).
  • Execució pas a pas (F7): executa una instrucció i s’atura. Útil per seguir el flux instrucció per instrucció.
  • Reinici (F12): torna el programa a l’estat inicial sense tornar a assemblar.

El programa hola.s de Secció 28.1 no té bucles: el nombre d’instruccions és fix i petit. És el programa que s’usarà per al recompte manual.


Exercici 28.3 (s1_3_1.md) Treballeu sobre el programa hola.s.

  1. Obriu Tools → Instruction Counter, connecteu-la al programa (Connect to Program) i executeu amb F5. Quin nombre d’instruccions executades reporta l’eina?
  2. Feu el recompte manual: llisteu totes les instruccions que s’executen, en ordre, indicant per a cada una si és una instrucció real o l’expansió d’una pseudoinstrucció. Quantes instruccions reals s’executen en total?
  3. El valor del recompte manual coincideix amb el que reporta l’Instruction Counter? Si hi ha diferència, expliqueu-ne la causa.

Indicació: la pseudoinstrucció la s’expandeix en dues instruccions reals. Useu el panell de codi desassemblat per veure exactament quines instruccions s’executen.

Solució 28.3.

  1. 7 instruccions.
  2. la a0, msg (auipc + addi, 2 instruccions reals), li a7, 4 (addi, 1), ecall (1), li a7, 93 (addi, 1), li a0, 0 (addi, 1), ecall (1). Total: 7 instruccions reals.
  3. Sí, coincideixen (7 = 7). Tot i que el codi font té 6 instruccions, el recompte manual dona 7 perquè la s’expandeix en dues instruccions reals.

Exercici 28.4 (s1_3_2.md) Assembleu suma.s i observeu el panell de memòria de dades.

  1. En quina adreça de memòria comença el vector V?
  2. En quina adreça exacta es troba V[3]? Quin és el seu contingut en hexadecimal?
  3. V[0] conté el valor -1. Quin valor hexadecimal té al panell de memòria? Expliqueu com s’obté aquesta representació.
  4. El panell mostra cada paraula com un valor hexadecimal de 32 bits. Quin ordre dels bytes (endianness) usa RV32I?

Solució 28.4.

  1. 0x10010000 (adreça per defecte del segment .data a RARS).
  2. 0x1001000C (= 0x10010000 + 3·4); conté 0xFFFFFFFC.
  3. 0xFFFFFFFF. S’obté aplicant a +1 la regla del canvi de signe en Ca2 (Secció 1.5.1): es complementen tots els bits d’1 (0x00000001 → 0xFFFFFFFE) i se suma 1; o, de manera equivalent, \(2^{32} - 1\) = 0xFFFFFFFF.
  4. RV32I és little-endian: el byte de menys pes s’emmagatzema a l’adreça més baixa.

28.4 Punts de ruptura

Un punt de ruptura (breakpoint) és una marca sobre una instrucció que fa que l’execució contínua (F5) s’aturi just abans d’executar-la. S’activa i es desactiva fent clic a la casella de l’esquerra del panell de codi desassemblat, a la fila de la instrucció triada.

Els punts de ruptura permeten inspeccionar l’estat del programa en un punt clau sense haver d’executar-lo pas a pas des del principi. Quan el programa s’atura, es pot continuar amb F5 (fins al següent punt de ruptura) o amb F7 (pas a pas).


Exercici 28.5 (s1_4_1.md) Treballeu sobre suma.s.

  1. Afegiu un punt de ruptura a la instrucció add t0, t0, t4 (la que acumula la suma). Executeu el programa amb F5. En quina iteració s’atura? Quins són els valors de t0 i t4 en aquest moment? Tingueu en compte que l’execució s’atura just abans d’executar la instrucció marcada.
  2. Premeu F5 repetidament fins al final. Anoteu el valor de t4 en cada aturada. Verifiqueu que els valors corresponen als elements del vector V en ordre.
  3. Quin és el valor final de t0 un cop ha acabat l’execució? Era el resultat esperat? Justifiqueu-lo.

Solució 28.5.

  1. S’atura a la primera iteració, amb t0 = 0 i t4 = -1.
  2. Els valors de t4 en cada aturada successiva són −1, −2, −3, −4, −5, −6, −7, −8, −9, −10: coincideixen exactament amb els elements de V en ordre.
  3. t0 = -55. És el resultat esperat: −1−2−3−4−5−6−7−8−9−10 = −55.

28.5 Depuració

Quan un programa assembla sense errors, però produeix un resultat incorrecte, cal depurar-lo: executar-lo pas a pas o amb punts de ruptura per identificar en quina instrucció el comportament no és l’esperat.

El procediment general és:

  1. Determineu el resultat esperat abans d’executar el programa.
  2. Identifiqueu un punt de control (per exemple, la instrucció d’acumulació d’un bucle) i afegiu-hi un punt de ruptura.
  3. En cada aturada, comproveu que els valors dels registres rellevants coincideixen amb el que s’esperava.
  4. Quan trobeu una discrepància, retrocediu un pas per identificar la instrucció causant. RARS ofereix Run → Backstep (F8) per desfer l’última instrucció executada sense haver de reiniciar.

Exercici 28.6 (s1_5_1.s i s1_5_1.md) El programa s1_5_1.s és una versió errònia del programa suma.s. Conté un únic error que fa que el resultat final de t0 no sigui -55.

s1_5_1.s
.data
V:      .word   -1, -2, -3, -4, -5, -6, -7, -8, -9, -10

.text
        li      t0, 0
        li      t1, 9
        li      t2, 10
        la      t3, V

bucle:
        bge     t1, t2, fi
        slli    t4, t1, 2
        add     t4, t3, t4
        lw      t4, 0(t4)
        add     t0, t0, t4
        addi    t1, t1, 1
        j       bucle

fi:
        li      a7, 93
        li      a0, 0
        ecall
  1. Sense executar el programa, analitzeu el codi i preveieu quin serà el resultat incorrecte de t0 i per quina raó.
  2. Executeu el programa pas a pas per confirmar la vostra predicció. En quina iteració s’observa per primera vegada un comportament incorrecte?
  3. Corregiu l’error (canvieu una única línia) i verifiqueu que el resultat és -55. Lliureu la versió corregida com s1_5_1.s.
  4. Al fitxer s1_5_1.md, descriviu: quina és la instrucció errònia, quin valor incorrecte produïa i com ho heu detectat.

Solució 28.6.

  1. L’error és li t1, 9 (hauria de ser li t1, 0): l’índex i comença a 9 en lloc de 0, per tant, el bucle només suma V[9] = -10. El resultat incorrecte previst és t0 = -10.
  2. El comportament incorrecte s’observa ja a la primera iteració: t1 val 9 des de l’inici i a t4 es llegeix V[9] = -10 en lloc de V[0] = -1.
  3. Cal canviar li t1, 9 per li t1, 0. Amb la correcció, t0 acaba valent -55.
  4. La instrucció errònia és li t1, 9; produïa t0 = -10 perquè només s’acumulava l’últim element del vector; es detecta comparant l’índex inicial esperat (0) amb el valor carregat a t1 a l’inici de l’execució pas a pas.

Exercici 28.7 (s1_5_2.s i s1_5_2.md) El programa s1_5_2.s calcula la mitjana entera dels elements del vector V (és a dir, la divisió entera de la suma entre el nombre d’elements). La divisió es fa per mitjà d’un segon bucle que resta repetidament el divisor del dividend mentre el dividend no sigui inferior al divisor.

El programa conté un únic error. El resultat correcte del quocient (registre t5) hauria de ser 5 (ja que \(\lfloor 55/10 \rfloor = 5\), i la suma dels elements de V = {1, 2, ..., 10} és 55).

s1_5_2.s
.data
V:      .word   1, 2, 3, 4, 5, 6, 7, 8, 9, 10

.text
        li      t0, 0           # t0 = suma = 0
        li      t1, 0           # t1 = i = 0
        li      t2, 10          # t2 = N = 10
        la      t3, V

bucle:
        bge     t1, t2, fi
        slli    t4, t1, 2
        add     t4, t3, t4
        lw      t4, 0(t4)
        add     t0, t0, t4
        addi    t1, t1, 1
        j       bucle

fi:
        li      t5, 0           # t5 = quocient = 0
        mv      t6, t2          # t6 = divisor = N

bucle2:
        blt     t0, t6, fi2     # si suma < divisor, acaba
        sub     t0, t6, t0      # suma -= divisor
        addi    t5, t5, 1
        j       bucle2

fi2:
        li      a7, 93
        li      a0, 0
        ecall
  1. Sense executar el programa, llegiu el codi del segon bucle (bucle2) i identifiqueu l’error. Què hauria de fer la instrucció errònia?
  2. Afegiu un punt de ruptura a la primera instrucció de bucle2. Executeu fins al punt de ruptura amb F5 i comproveu que t0 és 55 i t6 és 10.
  3. Continueu amb F7 pas a pas. Observeu el valor de t0 després de la instrucció sub. Es confirma el diagnòstic del punt a)?
  4. Corregiu l’error i verifiqueu que t5 val 5. Lliureu la versió corregida com s1_5_2.s.
  5. Al fitxer s1_5_2.md, descriviu: on heu col·locat el punt de ruptura i per quina raó, quina és la instrucció errònia i com l’heu corregit.

Solució 28.7.

  1. L’error és sub t0, t6, t0 (els operands estan invertits: hauria de ser sub t0, t0, t6). La instrucció hauria de calcular t0 = t0 - t6 (suma restant el divisor), no t6 - t0.
  2. A l’aturada, t0 = 55 (suma de V = {1, ..., 10}) i t6 = 10 (còpia de N).
  3. Després del sub erroni, t0 = 10 - 55 = -45. Això confirma el diagnòstic: en comptes de restar el divisor de la suma (55 − 10 = 45), es resta la suma del divisor, produint un valor negatiu que fa sortir del bucle massa aviat (amb t5 = 1 en lloc de 5).
  4. Amb sub t0, t0, t6, el bucle itera 5 vegades (55→45→35→25→15→5) i t5 acaba valent 5.
  5. Exemple de resposta: el punt de ruptura es col·loca a blt t0, t6, fi2 (primera instrucció de bucle2) perquè és el punt on es pot comprovar l’estat inicial de t0 i t6 abans de cap iteració; la instrucció errònia és sub t0, t6, t0, amb els operands invertits; es corregeix escrivint sub t0, t0, t6.