Exceptions and Interrupts

本文介绍RISC-V架构中核心对中断和异常的处理方式,包括自定义中断扩展、异常优先级、非屏蔽中断处理及嵌套中断机制等关键内容。

Exceptions and Interrupts

|corev| implements trap handling for interrupts and exceptions according to the RISC-V Privileged Specification, version 1.11.
The irq_i[31:16] interrupts are a custom extension.

When entering an interrupt/exception handler, the core sets the mepc CSR to the current program counter and saves mstatus.MIE to mstatus.MPIE.
All exceptions cause the core to jump to the base address of the vector table in the mtvec CSR.
Interrupts are handled in either direct mode or vectored mode depending on the value of mtvec.MODE. In direct mode the core
jumps to the base address of the vector table in the mtvec CSR. In vectored mode the core jumps to the base address
plus four times the interrupt ID. Upon executing an MRET instruction, the core jumps to the program counter previously saved in the
mepc CSR and restores mstatus.MPIE to mstatus.MIE.

The base address of the vector table must be aligned to 256 bytes (i.e., its least significant byte must be 0x00) and can be programmed
by writing to the mtvec CSR. For more information, see the :ref:cs-registers documentation.

The core starts fetching at the address defined by boot_addr_i. It is assumed that the boot address is supplied via a register
to avoid long paths to the instruction fetch unit.

Interrupt Interface

Interrupt interface signals describes the interrupt interface.

table:: Interrupt interface signals
name: Interrupt interface signals

SignalDirectionDescription
irq_i[31:0]inputActive high, level sensistive interrupt inputs.
Not all interrupt inputs can be used on
irq_i[10:8], irq_i[6:4] and irq_i[2:0] shall be
tied to 0 externally as they are reserved for
future standard use (or for cores which are not
Machine mode only) in the RISC-V Privileged
specification. irq_i[11], irq_i[7], and irq_i[3]
correspond to the Machine External
Interrupt (MEI), Machine Timer Interrupt (MTI),
and Machine Software Interrupt (MSI)
respectively. The irq_i[31:16] interrupts
are a
Basic (a.k.a. CLINT) interrupt scheme.

Interrupts

The irq_i[31:0] interrupts are controlled via the mstatus, mie and mip CSRs. |corev| uses the upper 16 bits of mie and mip for custom interrupts (irq_i[31:16]),
which reflects an intended custom extension in the RISC-V Basic (a.k.a. CLINT) interrupt architecture.
After reset, all interrupts, except for NMIs, are disabled.
To enable any of the irq_i[31:0] interrupts, both the global interrupt enable (MIE) bit in the mstatus CSR and the corresponding individual interrupt enable bit in the mie CSR need to be set. For more information, see the :ref:cs-registers documentation.

If multiple interrupts are pending, they are handled in the fixed priority order defined by the RISC-V Privileged Specification, version 1.11 (see Machine Interrupt Registers, Section 3.1.9).
The highest priority is given to the interrupt with the highest ID, except for the Machine Timer Interrupt, which has the lowest priority. So from high to low priority the interrupts are
ordered as follows:

  • store parity/checksum fault NMI (131)
  • load parity/checksum fault NMI (130)
  • store bus fault NMI (129)
  • load bus fault NMI (128)
  • irq_i[31]
  • irq_i[30]
  • irq_i[16]
  • irq_i[11]
  • irq_i[3]
  • irq_i[7]

The irq_i[31:0] interrupt lines are level-sensitive. The NMIs are triggered by load/store bus fault events and load/store parity/checksum fault events. To clear the irq_i[31:0] interrupts at the external source, |corev| relies on a software-based mechanism in which the interrupt handler signals completion of the handling routine to the interrupt source, e.g., through a memory-mapped register, which then deasserts the corresponding interrupt line.

In Debug Mode, all interrupts are ignored independent of mstatus.MIE and the content of the mie CSR.

|corev| can trigger the following interrupts as reported in mcause:

InterruptException CodeDescriptionScenario(s)
13Machine Software Interrupt (MSI)irq_i[3]
17Machine Timer Interrupt (MTI)irq_i[7]
111Machine External Interrupt (MEI)irq_i[11]
131-16Machine Fast Interruptsirq_i[31]-irq_i[16]
1128Load bus fault NMI (imprecise)data_err_i = 1 and data_rvalid_i = 1 for load
1129Store bus fault NMI (imprecise)data_err_i = 1 and data_rvalid_i = 1 for store
1130Load parity/checksum fault NMI (imprecise)Load parity/checksum fault (imprecise)
1131Store parity/checksum fault NMI (imprecise)Store parity/checksum fault (imprecise)

note::

Load bus fault, store bus fault, load parity/checksum fault and store parity/checksum fault are handled as imprecise non-maskable interrupts( as opposed to precise exceptions).

Non Maskable Interrupts

NMIs update mepc, mcause and mstatus similar to regular interrupts. However, as the faults that result in NMIs are imprecise, the contents of mepc is not guaranteed to point to the instruction after the faulted load or store.

note:

Specifically mstatus.mie will get cleared to 0 when an (unrecoverable) NMI is taken. [RISC-V-PRIV]_ does not specify the behavior of
mstatus in response to NMIs, see https://github.com/riscv/riscv-isa-manual/issues/756. If this behavior is
specified at a future date, then we will reconsider our implementation.

An NMI will occur when a load or store instruction experiences a bus fault. The fault resulting in an NMI is handled in an imprecise manner, meaning that the instruction that causes the fault is allowed to retire and the associated NMI is taken afterwards. NMIs are never masked by the MIE bit. NMIs are masked however while in debug mode or while single stepping with STEPIE = 0 in the dcsr CSR. This means that many instructions may retire before the NMI is visible to the core if debugging is taking place. Once the NMI is visible to the core, at most two instructions may retire before the NMI is taken. This is guaranteed, as the core will stop issuing new instructions when any interrupt, including NMI, is pending. This will eventually cause an interruptible time slot.

If an NMI becomes pending while in debug mode as described above, the NMI will be taken in the first available cycle after debug mode has been exited.

In case of bufferable stores, the NMI is allowed to become visible an arbitrary time after the instruction retirement. As for the case with debugging, this can cause several instructions to retire before the NMI becomes visible to the core.

When a data bus fault occurs, the first detected fault will be latched and used for mcause when the NMI is taken. Any new data bus faults occuring while an NMI is pending will be discarded. When the NMI handler is entered, new data bus faults may be latched.

While an NMI is pending, DCSR.nmip will be 1. Note that this CSR is only accessible from debug mode, and is thus not visible for machine mode code.

Exceptions

|corev| can trigger the following exceptions as reported in mcause:

InterruptException CodeDescriptionScenario(s)
01Instruction access faultExecution attempt from I/O region.
Execution attempt with address failing PMP check.
02Illegal instruction
03BreakpointInstruction address breakpoint.
Load/store/AMO address breakpoint.
Environment break.
05Load access faultNon-naturally aligned load access attempt to an I/O region.
Load-Reserved attempt to region without atomic support.
Load attempt with address failing PMP check.
07Store/AMO access faultNon-naturally aligned store access attempt to an I/O region.
Store-Conditional or Atomic Memory Operation (AMO) attempt
to region without atomic support.
Store attempt with address failing PMP check.
08Environment call from U-Mode (ECALL)
011Environment call from M-Mode (ECALL)
048Instruction bus faultinstr_err_i = 1 and instr_rvalid_i = 1 for instruction fetch
049Instruction parity/checksum faultinstr_gntpar_i, instr_rvalidpar, instr_rchk_i related errors

If an instruction raises multiple exceptions, the priority, from high to low, is as follows:

  • instruction address breakpoint (3)
  • instruction access fault (1)
  • instruction parity/checksum fault (49)
  • instruction bus fault (48)
  • illegal instruction (2)
  • environment call from U-Mode (8)
  • environment call from M-Mode (11)
  • environment break (3)
  • load/store/AMO address breakpoint (3)
  • store/AMO access fault (7)
  • load access fault (5)

Exceptions in general cannot be disabled and are always active. All exceptions are precise. Whether the PMP and PMA will actually cause exceptions depends on their configuration. |corev| raises an illegal instruction exception for any instruction in the RISC-V privileged and unprivileged specifications that is explicitly defined as being illegal according to the ISA implemented by the core, as well as for any instruction that is left undefined in these specifications unless the instruction encoding is configured as a custom |corev| instruction for specific parameter settings as defined in (see :ref:custom-isa-extensions). An instruction bus error leads to a precise instruction interface bus fault if an attempt is made to execute the instruction that has an associated bus error. Similarly an instruction fetch with a failing PMA or PMP check only leads to an instruction access exception if an actual execution attempt is made for it.

Nested Interrupt/Exception Handling

|corev| does support nested interrupt/exception handling in software.
The hardware automatically disables interrupts upon entering an interrupt/exception handler.
Otherwise, interrupts/exceptions during the critical part of the handler, i.e. before software has saved the mepc and mstatus CSRs, would cause those CSRs to be overwritten.
If desired, software can explicitly enable interrupts by setting mstatus.MIE to 1 from within the handler.
However, software should only do this after saving mepc and mstatus.
There is no limit on the maximum number of nested interrupts.
Note that, after enabling interrupts by setting mstatus.MIE to 1, the current handler will be interrupted also by lower priority interrupts.
To allow higher priority interrupts only, the handler must configure mie accordingly.

The following pseudo-code snippet visualizes how to perform nested interrupt handling in software.

isr_handle_nested_interrupts(id) {
  // Save mpec and mstatus to stack
  mepc_bak = mepc;
  mstatus_bak = mstatus;

  // Save mie to stack (optional)
  mie_bak = mie;

  // Keep lower-priority interrupts disabled (optional)
  mie = mie & ~((1 << (id + 1)) - 1);

  // Re-enable interrupts
  mstatus.MIE = 1;

  // Handle interrupt
  // This code block can be interrupted by other interrupts.
  // ...

  // Restore mstatus (this disables interrupts) and mepc
  mstatus = mstatus_bak;
  mepc = mepc_bak;

  // Restore mie (optional)
  mie = mie_bak;
}

Nesting of interrupts/exceptions in hardware is not supported.

Chapter 4: Processor Architecture. This chapter covers basic combinational and sequential logic elements, and then shows how these elements can be combined in a datapath that executes a simplified subset of the x86-64 instruction set called “Y86-64.” We begin with the design of a single-cycle datapath. This design is conceptually very simple, but it would not be very fast. We then introduce pipelining, where the different steps required to process an instruction are implemented as separate stages. At any given time, each stage can work on a different instruction. Our five-stage processor pipeline is much more realistic. The control logic for the processor designs is described using a simple hardware description language called HCL. Hardware designs written in HCL can be compiled and linked into simulators provided with the textbook, and they can be used to generate Verilog descriptions suitable for synthesis into working hardware. Chapter 5: Optimizing Program Performance. This chapter introduces a number of techniques for improving code performance, with the idea being that programmers learn to write their C code in such a way that a compiler can then generate efficient machine code. We start with transformations that reduce the work to be done by a program and hence should be standard practice when writing any program for any machine. We then progress to transformations that enhance the degree of instruction-level parallelism in the generated machine code, thereby improving their performance on modern “superscalar” processors. To motivate these transformations, we introduce a simple operational model of how modern out-of-order processors work, and show how to measure the potential performance of a program in terms of the critical paths through a graphical representation of a program. You will be surprised how much you can speed up a program by simple transformations of the C code. Bryant & O’Hallaron fourth pages 2015/1/28 12:22 p. xxiii (front) Windfall Software, PCA ZzTEX 16.2 xxiv Preface Chapter 6: The Memory Hierarchy. The memory system is one of the most visible parts of a computer system to application programmers. To this point, you have relied on a conceptual model of the memory system as a linear array with uniform access times. In practice, a memory system is a hierarchy of storage devices with different capacities, costs, and access times. We cover the different types of RAM and ROM memories and the geometry and organization of magnetic-disk and solid state drives. We describe how these storage devices are arranged in a hierarchy. We show how this hierarchy is made possible by locality of reference. We make these ideas concrete by introducing a unique view of a memory system as a “memory mountain” with ridges of temporal locality and slopes of spatial locality. Finally, we show you how to improve the performance of application programs by improving their temporal and spatial locality. Chapter 7: Linking. This chapter covers both static and dynamic linking, including the ideas of relocatable and executable object files, symbol resolution, relocation, static libraries, shared object libraries, position-independent code, and library interpositioning. Linking is not covered in most systems texts, but we cover it for two reasons. First, some of the most confusing errors that programmers can encounter are related to glitches during linking, especially for large software packages. Second, the object files produced by linkers are tied to concepts such as loading, virtual memory, and memory mapping. Chapter 8: Exceptional Control Flow. In this part of the presentation, we step beyond the single-program model by introducing the general concept of exceptional control flow (i.e., changes in control flow that are outside the normal branches and procedure calls). We cover examples of exceptional control flow that exist at all levels of the system, from low-level hardware exceptions and interrupts, to context switches between concurrent processes, to abrupt changes in control flow caused by the receipt of Linux signals, to the nonlocal jumps in C that break the stack discipline. This is the part of the book where we introduce the fundamental idea of a process, an abstraction of an executing program. You will learn how processes work and how they can be created and manipulated from application programs. We show how application programmers can make use of multiple processes via Linux system calls. When you finish this chapter, you will be able to write a simple Linux shell with job control. It is also your first introduction to the nondeterministic behavior that arises with concurrent program execution. Chapter 9: Virtual Memory. Our presentation of the virtual memory system seeks to give some understanding of how it works and its characteristics. We want you to know how it is that the different simultaneous processes can each use an identical range of addresses, sharing some pages but having individual copies of others. We also cover issues involved in managing and manipulating virtual memory. In particular, we cover the operation of storage allocators such as the standard-library malloc and free operations. CovBryant & O’Hallaron fourth pages 2015/1/28 12:22 p. xxiv (front) Windfall Software, PCA ZzTEX 16.2 Preface xxv ering this material serves several purposes. It reinforces the concept that the virtual memory space is just an array of bytes that the program can subdivide into different storage units. It helps you understand the effects of programs containing memory referencing errors such as storage leaks and invalid pointer references. Finally, many application programmers write their own storage allocators optimized toward the needs and characteristics of the application. This chapter, more than any other, demonstrates the benefit of covering both the hardware and the software aspects of computer systems in a unified way. Traditional computer architecture and operating systems texts present only part of the virtual memory story. Chapter 10: System-Level I/O. We cover the basic concepts of Unix I/O such as files and descriptors. We describe how files are shared, how I/O redirection works, and how to access file metadata. We also develop a robust buffered I/O package that deals correctly with a curious behavior known as short counts, where the library function reads only part of the input data. We cover the C standard I/O library and its relationship to Linux I/O, focusing on limitations of standard I/O that make it unsuitable for network programming. In general, the topics covered in this chapter are building blocks for the next two chapters on network and concurrent programming. Chapter 11: Network Programming. Networks are interesting I/O devices to program, tying together many of the ideas that we study earlier in the text, such as processes, signals, byte ordering, memory mapping, and dynamic storage allocation. Network programs also provide a compelling context for concurrency, which is the topic of the next chapter. This chapter is a thin slice through network programming that gets you to the point where you can write a simple Web server. We cover the client-server model that underlies all network applications. We present a programmer’s view of the Internet and show how to write Internet clients and servers using the sockets interface. Finally, we introduce HTTP and develop a simple iterative Web server. Chapter 12: Concurrent Programming. This chapter introduces concurrent programming using Internet server design as the running motivational example. We compare and contrast the three basic mechanisms for writing concurrent programs—processes, I/O multiplexing, and threads—and show how to use them to build concurrent Internet servers. We cover basic principles of synchronization using P and V semaphore operations, thread safety and reentrancy, race conditions, and deadlocks. Writing concurrent code is essential for most server applications. We also describe the use of thread-level programming to express parallelism in an application program, enabling faster execution on multi-core processors. Getting all of the cores working on a single computational problem requires a careful coordination of the concurrent threads, both for correctness and to achieve high performance翻译以上英文为中文
08-05
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

山猫Show

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值