Tema 9: Excepcions i interrupcions

27.1 Excepcions i interrupcions

Exemple 27.1: Solució: conceptes bàsics d’excepcions i interrupcions

Enunciat: Problema 18.1.

a) Una excepció és un esdeveniment causat per l’execució d’una instrucció (origen intern): per exemple, un accés a memòria amb adreça no alineada o una instrucció il·legal. Una interrupció és un esdeveniment causat per un dispositiu extern (origen extern): per exemple, un teclat que notifica que s’ha premut una tecla.

b) En cas d’excepció, mepc apunta a la instrucció causant (la que s’ha interromput sense completar-se). En cas d’interrupció, mepc apunta a la instrucció següent a la instrucció que s’estava executant (la instrucció en curs ha finalitzat completament i el PC ja s’ha incrementat).

c) Quan el processador detecta una excepció o interrupció, executa automàticament, per maquinari (Secció 9.3.1):

  1. Guarda el PC a mepc.
  2. Escriu el codi de causa a mcause (bit 31 = 0 per a excepcions, bit 31 = 1 per a interrupcions).
  3. Copia MIE a MPIE i posa MIE = 0 (deshabilita les interrupcions).
  4. Guarda el mode de privilegi actual a MPP i passa a mode M.
  5. Salta a l’adreça de la rutina de servei d’excepcions (RSE) continguda a mtvec.

d) L’RSE ha de desar tots els registres generals (x1–x31) sense excepció. A diferència d’una subrutina normal, el programa interromput no ha cedit el control voluntàriament i no pot saber quins registres modificarà l’RSE. Les convencions ABI (registres salvats vs. temporals) no s’apliquen.

Exemple 27.2: Solució: RSE mínima en assemblador

Enunciat: Problema 18.3. Vegeu l’estructura general de l’RSE a Secció 9.4.

RV32IZicsr
rse:
        # a) Reservar espai i desar tots els registres (x1-x31, excepte sp=x2)
        addi    sp, sp, -120

        sw      ra,   0(sp)
        sw      t0,   4(sp)
        sw      t1,   8(sp)
        sw      t2,  12(sp)
        sw      s0,  16(sp)
        sw      s1,  20(sp)
        sw      a0,  24(sp)
        sw      a1,  28(sp)
        sw      a2,  32(sp)
        sw      a3,  36(sp)
        sw      a4,  40(sp)
        sw      a5,  44(sp)
        sw      a6,  48(sp)
        sw      a7,  52(sp)
        sw      s2,  56(sp)
        sw      s3,  60(sp)
        sw      s4,  64(sp)
        sw      s5,  68(sp)
        sw      s6,  72(sp)
        sw      s7,  76(sp)
        sw      s8,  80(sp)
        sw      s9,  84(sp)
        sw      s10, 88(sp)
        sw      s11, 92(sp)
        sw      t3,  96(sp)
        sw      t4, 100(sp)
        sw      t5, 104(sp)
        sw      t6, 108(sp)
        sw      gp, 112(sp)
        sw      tp, 116(sp)

        # b) Llegir mcause i comprovar si és ecall des de mode U (codi 8)
        csrr    t0, mcause
        li      t1, 8
        beq     t0, t1, handler_ecall

        # Causa desconeguda: salt al gestor d'error genèric
        j       handler_error

handler_ecall:
        # Tractament de la crida al sistema...
        # Incrementar mepc en 4 per continuar a la instrucció següent
        csrr    t0, mepc
        addi    t0, t0, 4
        csrw    mepc, t0
        j       rse_ret

handler_error:
        # Tractament d'error genèric...
        j       rse_ret

rse_ret:
        # c) Restaurar tots els registres
        lw      ra,   0(sp)
        lw      t0,   4(sp)
        lw      t1,   8(sp)
        lw      t2,  12(sp)
        lw      s0,  16(sp)
        lw      s1,  20(sp)
        lw      a0,  24(sp)
        lw      a1,  28(sp)
        lw      a2,  32(sp)
        lw      a3,  36(sp)
        lw      a4,  40(sp)
        lw      a5,  44(sp)
        lw      a6,  48(sp)
        lw      a7,  52(sp)
        lw      s2,  56(sp)
        lw      s3,  60(sp)
        lw      s4,  64(sp)
        lw      s5,  68(sp)
        lw      s6,  72(sp)
        lw      s7,  76(sp)
        lw      s8,  80(sp)
        lw      s9,  84(sp)
        lw      s10, 88(sp)
        lw      s11, 92(sp)
        lw      t3,  96(sp)
        lw      t4, 100(sp)
        lw      t5, 104(sp)
        lw      t6, 108(sp)
        lw      gp, 112(sp)
        lw      tp, 116(sp)

        addi    sp, sp, 120

        # Retornar: MIE←MPIE, mode←MPP, PC←mepc
        mret

Observacions:

  • L’etiqueta rse ha de ser l’adreça que es configura a mtvec (camp BASE) durant la inicialització.
  • El salt j és una pseudoinstrucció equivalent a jal x0, offset.
Exemple 27.3: Solució: mepc, mtvec i ajust de retorn

Enunciat: Problema 18.4.

a) La instrucció lw t0, 0(t1) a 0x80001000 genera una excepció en executar-se (adreça no alineada). La instrucció s’avorta sense completar-se i el PC no s’incrementa. El maquinari guarda l’adreça de la instrucció causant:

\[\texttt{mepc} = \texttt{0x80001000}\]

b) La interrupció de temporitzador es detecta en acabar la instrucció lw t0, 0(a0) (el PC ja s’ha incrementat). El maquinari guarda l’adreça de la instrucció següent:

\[\texttt{mepc} = \texttt{0x80002008}\]

En retornar amb mret, el processador salta a mepc, és a dir, a 0x80002008: la instrucció que seguia el lw.

c) En mode directe (MODE = 0), totes les excepcions i interrupcions salten a BASE. L’RSE llegeix mcause per identificar la causa i derivar el control al gestor específic.

En mode vectoritzat (MODE = 1), les interrupcions salten a BASE + 4 × causa; les excepcions sempre salten a BASE.

Per a una interrupció de temporitzador en mode M (codi de causa 7):

\[\text{adreça} = \texttt{BASE} + 4 \times 7 = \texttt{BASE} + 28\]

d) Sense l’increment, mret retornaria a la mateixa instrucció ecall, que causaria una nova excepció indefinidament. L’RSE ha d’incrementar mepc perquè l’ecall ja ha estat atesa i el programa ha de continuar a la instrucció següent:

RV32IZicsr
        csrr    t0, mepc            # llegir l'adreça de retorn
        addi    t0, t0, 4           # apuntar a la instrucció següent
        csrw    mepc, t0            # actualitzar mepc

27.2 Crides al sistema

Exemple 27.4: Solució: crides al sistema bàsiques a RARS

Enunciat: Problema 18.5. Els codis de servei de RARS són a RISC-V 9.13.

a) Imprimir l’enter de t0:

RV32I
        mv      a0, t0              # argument: enter a imprimir
        li      a7, 1               # codi: print_int
        ecall

b) Llegir un enter i deixar el resultat a t1:

RV32I
        li      a7, 5               # codi: read_int
        ecall                       # a0 ← enter llegit
        mv      t1, a0              # moure el resultat a t1

c) Imprimir la cadena de caràcters amb adreça base a t2:

RV32I
        mv      a0, t2              # argument: adreça del string
        li      a7, 4               # codi: print_string
        ecall

d) Finalitzar el programa:

RV32I
        li      a7, 93              # codi: exit2
        li      a0, 0               # codi de sortida: 0
        ecall                       # no retorna

Nota: a EC la sortida es fa sempre amb exit2, no amb exit (codi 10) — vegeu RISC-V 2.19.

27.3 Entrada/sortida

Exemple 27.5: Solució: conceptes d’entrada/sortida

Enunciat: Problema 18.8.

a) En la sincronització per enquesta (polling), el processador consulta repetidament el registre d’estat del dispositiu fins que l’operació acaba. És simple, però desaprofita el processador durant l’espera. En la sincronització per interrupcions, el dispositiu notifica el processador quan ha acabat, i el processador pot fer feina útil entremig. És més eficient, però requereix configurar els permisos d’interrupció i escriure una RSE.

b) Per habilitar les interrupcions en RISC-V cal:

  1. Habilitar el permís específic del tipus d’interrupció al registre mie (per exemple, MEIE = bit 11 per a interrupcions externes).
  2. Habilitar el permís global MIE (bit 3 de mstatus).
RV32IZicsr
        # Habilitar interrupcions externes (MEIE = bit 11 de mie)
        li      t0, 0x800           # bit 11 (MEIE)
        csrrs   x0, mie, t0

        # Habilitar permís global (MIE = bit 3 de mstatus)
        li      t0, 0x8             # bit 3 (MIE)
        csrrs   x0, mstatus, t0

c) El bit MIE (bit 3 de mstatus) és el permís global: si val 0, cap interrupció no s’atén, independentment de la resta de bits. El bit MTIE (bit 7 de mie) és la màscara específica de les interrupcions de temporitzador en mode M: si val 0, les interrupcions de temporitzador queden inhibides fins i tot quan MIE = 1. Cal que tots dos valguin 1 (i el bit corresponent de mip estigui actiu) perquè una interrupció de temporitzador sigui atesa.

Exemple 27.6: Solució: RSE per a interrupcions de teclat

Enunciat: Problema 18.10.

a) Cal activar MEIE (bit 11 de mie) i MIE (bit 3 de mstatus):

RV32IZicsr
        # Habilitar interrupcions externes en mode M (MEIE = bit 11 de mie)
        li      t0, 0x800           # bit 11 (MEIE)
        csrrs   x0, mie, t0

        # Habilitar permís global d'interrupcions (MIE = bit 3 de mstatus)
        li      t0, 0x8             # bit 3 (MIE)
        csrrs   x0, mstatus, t0

b) RSE mínima per a la interrupció de teclat:

RV32IZicsr
.data
car:    .word   0

.text
rse_teclat:
        # Desar tots els registres generals (x1-x31, excepte sp=x2)
        addi    sp, sp, -120

        sw      ra,   0(sp)
        sw      t0,   4(sp)
        sw      t1,   8(sp)
        sw      t2,  12(sp)
        sw      s0,  16(sp)
        sw      s1,  20(sp)
        sw      a0,  24(sp)
        sw      a1,  28(sp)
        sw      a2,  32(sp)
        sw      a3,  36(sp)
        sw      a4,  40(sp)
        sw      a5,  44(sp)
        sw      a6,  48(sp)
        sw      a7,  52(sp)
        sw      s2,  56(sp)
        sw      s3,  60(sp)
        sw      s4,  64(sp)
        sw      s5,  68(sp)
        sw      s6,  72(sp)
        sw      s7,  76(sp)
        sw      s8,  80(sp)
        sw      s9,  84(sp)
        sw      s10, 88(sp)
        sw      s11, 92(sp)
        sw      t3,  96(sp)
        sw      t4, 100(sp)
        sw      t5, 104(sp)
        sw      t6, 108(sp)
        sw      gp, 112(sp)
        sw      tp, 116(sp)

        # Comprovar que és una interrupció externa (bit 31 de mcause = 1, codi = 11)
        csrr    t0, mcause
        li      t1, 0x8000000B      # bit31=1, codi=11 (interrupció externa mode M)
        bne     t0, t1, rse_ret_teclat

        # Comprovar bit MEIP de mip (bit 11): petició externa pendent
        csrr    t0, mip
        li      t1, 0x800           # bit 11 (MEIP)
        and     t0, t0, t1
        beqz    t0, rse_ret_teclat

        # Confirmar que la interrupció prové del teclat (registre d'estat, bit 0)
        li      t0, 0xF0001000      # adreça registre d'estat del teclat
        lw      t1, 0(t0)
        andi    t1, t1, 1           # bit 0: tecla disponible
        beqz    t1, rse_ret_teclat

        # Llegir el caràcter del registre de dades i desar-lo a 'car'
        li      t0, 0xF0001004      # adreça registre de dades del teclat
        lw      t1, 0(t0)           # llegir el caràcter
        la      t2, car
        sw      t1, 0(t2)           # desar a la variable global

rse_ret_teclat:
        # Restaurar tots els registres generals
        lw      ra,   0(sp)
        lw      t0,   4(sp)
        lw      t1,   8(sp)
        lw      t2,  12(sp)
        lw      s0,  16(sp)
        lw      s1,  20(sp)
        lw      a0,  24(sp)
        lw      a1,  28(sp)
        lw      a2,  32(sp)
        lw      a3,  36(sp)
        lw      a4,  40(sp)
        lw      a5,  44(sp)
        lw      a6,  48(sp)
        lw      a7,  52(sp)
        lw      s2,  56(sp)
        lw      s3,  60(sp)
        lw      s4,  64(sp)
        lw      s5,  68(sp)
        lw      s6,  72(sp)
        lw      s7,  76(sp)
        lw      s8,  80(sp)
        lw      s9,  84(sp)
        lw      s10, 88(sp)
        lw      s11, 92(sp)
        lw      t3,  96(sp)
        lw      t4, 100(sp)
        lw      t5, 104(sp)
        lw      t6, 108(sp)
        lw      gp, 112(sp)
        lw      tp, 116(sp)

        addi    sp, sp, 120
        mret

c) El codi de causa (mcause, codi 11) i el bit MEIP de mip indiquen que hi ha una interrupció externa pendent, però no quin dispositiu específic l’ha causada. Per identificar que la interrupció prové del teclat cal llegir directament el registre d’estat del dispositiu (0xF0001000, bit 0): és l’únic lloc on el controlador del teclat indica que té una dada disponible. En sistemes amb múltiples dispositius d’E/S, l’RSE ha d’interrogar cadascun per determinar quin ha generat la petició.

27.4 Fallada de pàgina

Exemple 27.7: Solució: fallada de pàgina i TLB gestionat per programari

Enunciat: Problema 18.11, apartats a–b.

a) Una fallada de pàgina escriu a mcause (RISC-V 9.4):

  • En lectura de dada: mcause = 13 (load page fault).
  • En escriptura: mcause = 15 (store/AMO page fault).

En tots dos casos, mtval conté l’adreça virtual que ha causat la fallada (Secció 9.7.2).

Cal recordar que, a RISC-V, la fallada de pàgina només es produeix quan la pàgina no és a la memòria física (bit V = 0 a la PTE): la fallada de TLB amb la pàgina present la resol el recorregut de la taula de pàgines (page-table walker) sense interrompre el programa (Essencial 9.9).

b) El flux que ha de seguir l’RSE (la rutina completa és a Secció 9.7.3.2):

  1. Llegir mtval per obtenir l’adreça virtual que ha causat la fallada.
  2. Extreure’n el VPN (els bits per sobre del desplaçament de pàgina) i localitzar la PTE corresponent a la taula de pàgines del procés actiu (a partir del registre de taula de pàgines; satp en mode S).
  3. Si l’adreça no pertany a cap regió vàlida del procés, avortar el programa (segmentation fault).
  4. Si la pàgina és vàlida però no present (\(V = 0\)): copiar-la del disc a un marc lliure (reemplaçant-ne un si cal, i escrivint-lo prèviament al disc si té \(D = 1\)) i actualitzar la PTE (\(V \leftarrow 1\), \(PPN \leftarrow\) marc assignat, \(D \leftarrow 0\)).
  5. Executar sfence.vma perquè el TLB no conservi cap còpia obsoleta de la PTE i el recorregut de la taula de pàgines la torni a llegir.
  6. No incrementar mepc: la instrucció causant s’ha de reexecutar; en la reexecució, el recorregut de la taula de pàgines trobarà la PTE vàlida i l’accés es completarà.