Skip to content

Latest commit

 

History

History
414 lines (344 loc) · 13.2 KB

File metadata and controls

414 lines (344 loc) · 13.2 KB

Lecture 11

Exceptions and Interrupts.

Lecture

Slides (PDF, PPTX).

Outline

  • Exceptions and interrupts

Examples:

Workshop

Outline

  • Control and Status Registers
  • System instructions
  • Exceptions
  • Exception handling
  • Interrupts

Exceptions and Interrupts

Exception is an an unscheduled event that disrupts program execution. Interrupt is an exception that comes from outside of the processor. (Some architectures use the term interrupt for all exceptions.) Exceptions require dealing with special system instructions and registers.

Control and Status Registers (CSRs)

The Control and Status Registers (CSRs) are system registers provided by RISC-V to control monitor system states. CSR’s can be read, written and bits can be set/cleared. Each CSR has a special name and is assigned a unique function. In this course, we focus on the user privilege level. We will use user-level CSRs to handle user-level exceptions.

User-level CSRs:

| Number | Priviledge | Name | Description | | User Trap Setup | | 0x000 | URW | ustatus | User status register. | | 0x004 | URW | uie | User interrupt-enable register. | | 0x005 | URW | utvec | User trap handler base address. | | User Trap Handling | | 0x040 | URW | uscratch | Scratch register for user trap handlers. | | 0x041 | URW | uepc | User exception program counter. | | 0x042 | URW | ucause | User trap cause. | | 0x043 | URW | utval | User bad address or instruction. | | 0x044 | URW | uip | User interrupt pending. | | User Floating-Point CSRs | | 0x001 | URW | fflags | Floating-Point Accrued Exceptions. | | 0x002 | URW | frm | Floating-Point Dynamic Rounding Mode. | | 0x003 | URW | fcsr | Floating-Point Control and Status Register. | | User Counter/Timers | | 0xC00 | URO | cycle | Cycle counter for RDCYCLE instruction. | | 0xC01 | URO | time | Timer for RDTIME instruction. | | 0xC02 | URO | instret | Instructions-retired counter for RDINSTRET instruction. | | 0xC80 | URO | cycleh | Upper 32 bits of cycle, RV32 only. | | 0xC81 | URO | timeh | Upper 32 bits of time, RV32 only. | | 0xC82 | URO | instreth | Upper 32 bits of instret, RV32 only. |

System Instructions

CSR Instructions:

|-----------------------|-| | csrrc t0, fcsr, t1 | Read/Clear CSR: read from the CSR into t0 and clear bits of the CSR according to t1 | | csrrci t0, fcsr, 10 | Read/Clear CSR Immediate: read from the CSR into t0 and clear bits of the CSR according to a constant | | csrrs t0, fcsr, t1 | Read/Set CSR: read from the CSR into t0 and logical or t1 into the CSR | | csrrsi t0, fcsr, 10 | Read/Set CSR Immediate: read from the CSR into t0 and logical or a constant into the CSR | | csrrw t0, fcsr, t1 | Read/Write CSR: read from the CSR into t0 and write t1 into the CSR | | csrrwi t0, fcsr, 10 | Read/Write CSR Immediate: read from the CSR into t0 and write a constant into the CSR |

CSR Pseudo Instructions:

|-----------------------|-| | csrc t1, fcsr | Clear bits in control and status register | | csrci fcsr, 100 | Clear bits in control and status register | | csrr t1, fcsr | Read control and status register | | csrs t1, fcsr | Set bits in control and status register | | csrsi fcsr, 100 | Set bits in control and status register | | csrw t1, fcsr | Write control and status register | | csrwi fcsr, 100 | Write control and status register |

System Instructions:

|-----------------------|-| | ebreak | Pause execution (at a breakpoint)| | ecall | Issue a system call : Execute the system call specified by value in a7 | | uret | Return from handling an interrupt or exception (to uepc) | | wfi | Wait for interrupt |

Exceptions

When an exception occurs the PC is written to the uepc register and the exception cause code is written to the ucause.

Exceptions Supported in RARS:

  • INSTRUCTION_ADDR_MISALIGNED (ucause = 0)

    Code:

        j main
    end:
        li a7, 10
        ecall
    main:
        la t0, end
        addi t0, t0, 2
        jr t0

    Result:

    Error in : Instruction load alignment error
    ucause = 0x0
    uepc = 0x00400006
    
  • INSTRUCTION_ACCESS_FAULT (ucause = 1)

    Code:

      .data
    data:
      .word 99
      .word 100
    .text
      la t0, data
      jr t0

    Result:

    Error in : Instruction load access error
    ucause = 0x00000001
    uepc = 0x10010000
    
  • ILLEGAL_INSTRUCTION (ucause = 2)

    Code:

      .text
    main:
      li    t0, 8
      csrrs zero, cycle, t0

    Result:

    Error in : Runtime exception at 0x00400004: Attempt to write to read-only CSR
    ucause = 0x00000002
    uepc = 0x00400004
    
  • LOAD_ADDRESS_MISALIGNED (ucause = 4)

    Code:

      .data
      .space 2
      .align 0
    data:
      .word 0xDEADBEEF
      .text
    main:
      la t0, data
      lw t1, 0(t0)

    Result:

    Error in: Load address not aligned to word boundary 0x10010002
    ucause = 0x00000004
    uepc = 0x00400008
    utval = 0x10010002
    
  • LOAD_ACCESS_FAULT (ucause = 5)

    Code:

    .text
    main:
      la t0, main
      lw t1, 0(t0)

    Result:

    Error in: Runtime exception at 0x00400008: Cannot read directly from text segment!0x00400000
    ucause = 0x00000005
    uepc = 0x00400008
    utval = 0x00400000
    
  • STORE_ADDRESS_MISALIGNED (ucause = 6)

    Code:

      .data
      .space 2
      .align 0
    data:
      .word 0
      .text
    main:
      la t0, data
      li t1, 0xDEADBEEF
      sw t1, 0(t0)

    Result:

    Error in: Runtime exception at 0x00400010: Store address not aligned to word boundary 0x10010002
    ucause = 0x00000006
    uepc = 0x00400010
    utval = 0x10010002
    
  • STORE_ACCESS_FAULT (ucause = 7)

    Code:

    .text
    main:
      la t0, main
      li t1, 0xDEADBEEF
      sw t1, 0(t0)

    Result:

    Error in: Runtime exception at 0x00400010: Cannot write directly to text segment!0x00400000
    ucause = 0x00000007
    uepc = 0x00400010
    utval = 0x00400000
    
  • ENVIRONMENT_CALL (ucause = 8)

    Code:

      .text
    main:
      li a7, 100
      ecall

    Result:

    Error in: Runtime exception at 0x00400008: invalid or unimplemented syscall service: 100 
    ucause = 0x00000004
    uepc = 0x00400008
    

Exception Handling

When an exception occurs the following actions are performed:

  • the uie (interrupt enable) bit in the status word is set to 0;
  • the ucause register is set to indicate which event has occurred;
  • the uepc is set to the last instruction that was executing when system trapped;
  • the PC is set to utvec value; in case of vectored exception handling, the PC is set utvec base address + 4 * utcause.

In order to have a working exception handler, the program must:

  • set utvec to the address of the handler code (the lowest two bits are special);
  • set the bits corresponding to the handled interrupts in uie;
  • set the interrupt enable (lowest) bit in ustatus to enable the handler.

The simplest user-level exception handler that just returns control to the next (PC+4) instruction:

handler:
     # Just ignore it by moving uepc to the next instruction
     csrrw t0, uepc, zero
     addi  t0, t0, 4
     csrrw zero, uepc, t0
     uret

The user-level handler can be registered in the following way:

     la     t0, handler      # load handler address to t0
     csrrw  zero, utvec, t0  # set utvec to the handlers address
     csrrsi zero, ustatus, 1 # set interrupt enable bit in ustatus

Interrupts are exceptions that come from external devices such as I/O devices. These events can be handled. To do this, an interrupt must be enabled in the device and a corresponding bit must be set in uie. See the examples to learn how this works.

Tasks

  1. Study the theory and examples on the current workshop.

  2. Implement an exception handler that prints a message that explains the reason of an exception (the list of exceptions with descriptions is above).

  3. Image how the try-catch construct is implemented in high-level languages. Then write a program that implements a simple function with an exception handler. The function takes an argument that specifies what exception it will raise (0 - no exception, 1 - some exception from list above, 2 - some other exception from the list). The function must return the exception cause or 0 if no exception has occurred. The program prints the exception cause.

    Pseudocode for the task looks like this:

    int testFunc(int arg) {
      try {
        if (arg == 1) {
          // divide by zero (or some other)
        }
        else if (arg == 2) {
          // read unaligned address (or some other)
        }
        // else if (arg == N) {
        //  // raise some other exception
        // }
      } catch(const KnownException& e) {
        return e.cause();
      }
      return 0;
    }
    
    int main() {
      int a = readInt();
      int b = testFunc(a);
      printInt(a);
      return 0;
    }
    
  4. See example timer.s. Write a program that waits for timer interrupts and counts them. Input data: m is the limit on number of interrupts to process, t is the interval between interrupts in milliseconds. The program exits when the number of handler interrupts reaches the limit.

    Hint: Use the Tools | Timer Tool RARS extension. See its help. The MMIO address to get the current time 0xFFFF0018; the MMIO address the set the time for the next interrupt is 0xFFFF0020. To set up the cycle of processing interrupts, the following must be done:

    • The address of your interrupt handler must be stored in the utvec CSR.
    • The fourth bit of the uie CSR must be set to 1 (i.e. ori uie, uie, 0x10).
    • The zeroth bit of the ustatus CSR must be set to 1 (i.e. ori ustatus, ustatus, 0x1).
    • The time for the next interrupt must be written to 0xFFFF0020.
    • When an interrupt is handled the time for the next interrupt must be updated.

Homework

NOTE: Handling exception ENVIRONMENT_CALL requires a patch in RARS. The patched version can be downloaded here.

Solve the following tasks and submit them into Ejudge:

  1. NoError

  2. NewEcall. Write an exception handler with label handler:, which implements three "new system calls" (100, 101, and 102) for working with "hidden registers":

    • 100 (a0 = size). Called only once. Allocates memory with size of size machine words ("hidden registers") and saves the size internally for further use. Returns nothing.
    • 101 (a0 = number). Reads a value of a "hidden register" with index number and saves it into a0. If number >= size (acltually, common case), the index is calculated as number % size.
    • 102 (a0 = number, a1 = value). Writes the value into "hidden register" with index number % size.

    You need to implement an exception handler that checks whether ucause equals to ENVIRONMENT_CALL and gets system call ID from a7. Then it must perform actions based on the system call and its arguments in a0/a1. The handler must preserve the state of registers it uses (save and restore them). The handler will be merged with test program new_ecall.s (see how it works).

    Input:

    8
    1
    1234
    -9
    -2
    1
    4213
    2
    -1
    -7
    -2
    -1
    0
    

    Output:

    1234
    0
    0
    -1
    4213
    

## References

* Krste	Asanović. [Interrupts](
https://riscv.org/wp-content/uploads/2024/12/Tue0900_RISCV-20160712-Interrupts.pdf).
* Chapter 6: “N” Standard Extension for User-Level Interrupts in 
[The RISC-V Instruction Set Manual Volume II: Privileged Architecture](
https://github.com/riscv/riscv-isa-manual/releases/latest).
* [RISC-V Assembly Programmer's Manual](https://github.com/riscv/riscv-asm-manual/blob/master/riscv-asm.md).
* The Processor. Chapter 4 in [[CODR]](../../books.md#codr).
* Pipelining: Basic and Intermediate Concepts. Appendix C in [[CAQA]](../../books.md#caqa).
* Instruction-Level Parallelism and Its Exploitation. Chapter 3 in [[CAQA]](../../books.md#caqa).
* [RISC-V Assembly Language Programmer Manual. Part I](https://shakti.org.in/docs/risc-v-asm-manual.pdf).
* [Interrupt](https://en.wikipedia.org/wiki/Interrupt) (Wikipedia).