Log in

No account? Create an account
Первый черновик новой лабораторной работы по MIPSfpga 2.0 "Введение в прерывания" - Юрий Панчул [entries|archive|friends|userinfo]
Money can buy bandwidth. Latency requires bribing God.

[ website | My Website ]
[ userinfo | livejournal userinfo ]
[ archive | journal archive ]

Первый черновик новой лабораторной работы по MIPSfpga 2.0 "Введение в прерывания" [Jan. 23rd, 2017|08:24 pm]
Yuri Panchul

Первый черновик новой лабораторной работы по MIPSfpga 2.0 "Введение в прерывания". Если вы считаете, что я не самый великий эксперт по английскому в ЖЖ, то вы можете опровергнуть мою идеальность. Опровержения принимаются в форме методически выписанного списка моих ошибок, а также стилистически переработанных предложений и абзацев:

Это первый мобильный компьютер в истории человечества: его перевозили на двух тягачах, один 12 тонн, а второй 8 тонн:

MIPSfpga 2.0. Lab YP3 - Introducing interrupts

1. Introduction

This lab shows the basic usage of interrupts in MIPS CPUs. The lab also demonstrates how the interrupts can offload the processor from constantly polling I/O ports. Such offloading increases the number of cycles available to the processor to spend on computation and other non-I/O tasks. The lab example can be used as a basis for a number of follow-up exercises that explore MIPS CPU features related to the interrupts. Another group of follow-up exercises can link the interrupts to software parallelism and context switching in operating systems. A number of recommended follow-up exercises are listed at the end of this lab.

2. The theory of operation

Interrupt is one of the key concepts in computer programming and system design. Interrupt is basically a feature of a CPU that forces the processor to suspend the regular flow of instruction execution and to jump to a certain program address in response to some external event. The external event is usually a change in a hardware signal from outside of the CPU. The memory location (or, in some processors, an index in an array of memory locations) where the processor jumps to, is called the interrupt vector. The piece of code where the control goes after landing on the interrupt vector, is called the interrupt service routine, or ISR. After the ISR is executed, the control goes back to the program location that was executed by the processor when it received the interrupt.

The external event that causes the interrupt can be a tick of a timer clock. Such timer interrupts are useful to organize task switching in a system where several software tasks, or programs, share the same processor, which switches between them. Another example of an external event that causes an interrupt is a signal about the completion of an input/output operation. Such I/O interrupts are useful to offload the regular program flow from constant polling of I/O registers, checking their status, as shown on Figure 1.

Figure 1. The action of an I/O interrupt. The source of the figure: http://virtualirfan.com/history-of-interrupts.

Interrupt, sometimes called more specifically hardware interrupt, is a special case of a more general term "exception". An exception is suspending the regular instruction flow and jumping to a vector in response not only to an external signal, but also to some internal conditions of the CPU, the conditions that require immediate attention. Such conditions include errors: arithmetic overflows, accessing out of range address, running a privileged instruction in non-privileged (user) mode, bus errors, and other unusual conditions. Some of those errors should terminate the offending program, while others should cause the program to recover, though the action of the exception service routine. Some of those conditions, like memory address exceptions, may be not considered errors at all, but parts of the mechanics of virtual memory implementation. There are also so-called software interrupts, the exceptions that are intentionally caused by the program to request the services of the operating system. Exceptions are also used in the processor's debug interface. During this lab we will deal strictly with "true" hardware interrupts. To learn about the other aspects of exceptions, please consult the core and architecture documentation.

Historically, the need for exceptions and interrupts was so obvious that they appeared very early in the history of computers, as shown on Figure 2.

Figure 2. The history of exceptions and interrupts. The source of the figure: http://virtualirfan.com/history-of-interrupts.

According to the article Interrupts by Mark Smotherman (https://people.cs.clemson.edu/~mark/interrupts.html#dyseac), the first computer that employed I/O interrupts was DYSEAC, the second version of SEAC, the Standards Electronic Automatic Computer. According to Wikipedia (https://en.wikipedia.org/wiki/DYSEAC), "DYSEAC was a first-generation computer built by the National Bureau of Standards for the US Army Signal Corps. It went into operation in April 1954". According to Smotherman, DYSEAC was perhaps also the first mobile computer, carried in two tractor trailers at 12 and 8 tons, as shown on Figure 3.

Figure 3. DYSEAC, the first computer with I/O interrupts. The source of the figure: http://ed-thelen.org/comp-hist.

3. Lab steps

This section outlines the sequence of steps, necessary to complete the lab. Almost all generic steps in this lab are the same as in MIPSfpga 2.0 Lab YP1. Using MIPSfpga with Serial Loader Flow that does not require BusBlaster board and OpenOCD software. Such generic steps are not described in this section. Only the steps different from Lab YP1 are explained in details.

3.1. Briefly review the reference materials

Look into Appendix A. A list of recommended materials to review before and during the interrupt lab. Briefly review the listed materials to get understanding where to look for the reference information during the lab.

3.2. Review the information about interrupt-related hardware signals

Review MIPS32® microAptiv™ UP Integrator's Guide, Chapter 4: Interrupt Interface. This manual is included in MIPSfpga package. The most important information is the description of interrupt pin signal SI_Int used in the lab. In the course of the lab this multi-bit signal is connected to the buttons on FPGA board. This allows triggering different interrupt by pressing the corresonding buttons.

3.3. Setup the hardware configuration parameters

Modify the configuration parameters in the file system_rtl/mfp_ahb_lite_matrix_config.vh as follows:

3.4. Review the lab-specific hardware modifications

Review the following fragment of system_rtl/mfp_system.v:

3.5. Connect the board to the computer

For Digilent boards, such as Nexys4, Nexys4 DDR or Basys3, this step is obvious. For Altera/Terasic boards some additional steps required:

  1. Connect USB-to-UART connector to FPGA board. Either FT232RL or PL2303TA that you can by from AliExpress or Radio Shack will do the job. TX output from the connector (green wire on PL2303TA) should go to pin 3 from right bottom on Terasic DE0, DE0-CV, DE1, DE2-115 (right top on DE0-Nano) and GND output (black wire on PL2303TA) should be connected to pin 6 from right bottom on Terasic DE0, DE0-CV, DE1, DE2-115 (right top on DE0-Nano). Please consult photo picture in Lab YP1 to avoid short-circuit or other connection problems.
  2. For FT232RL connector: make sure to set 3.3V/5V jumper on FT232RL part to 3.3V.
  3. For the boards that require external power in addition to the power that comes from USB, connect the power supply. The boards that require the extra power supply include Terasic DE2-115.
  4. Connect FPGA board to the computer using main connection cable provided by the board manufacturers. Make sure to put USB cable to the right jack when ambiguity exists (such as in Terasic DE2-115 board).
  5. Make sure to power the FPGA board (turn on the power switch) before connecting the UART cable from USB-to-UART connector to the computer. Failing to do so may result in electric damage to the board.
  6. Connect USB-to-UART connector to FPGA board.

3.6. Run the synthesis and configure the FPGA with the synthesized MIPSfpga system

This step is identical to the synthesis step in Lab YP1

3.7. Go to the lab directory and clean it up

Under Windows:

cd programs\lab_yp3

Under Linux:

cd programs/lab_yp3

3.8. Review the portion of the lab program code that does not use interrupts

The main() function is located in file programs/03_interrupts/main.c. This function is executed after the reset and running the boot sequence. The main function in this lab simply runs a counter and outputs its value on a multi-digit seven-segment display on FPGA board.

The output value is constructed from both high and low bits of the counter. This allows the student to observe the digits on the display changing when running the synthesized system with either fast clock (50 MHz) and ultra-slow clock (0.75 Hz or 12.5 Hz).

Depending on the position of a switch 2, the program can either run with or without interrupts. In both cases it resets the counter to predefined values when either button 0 or button 1 is pressed.

Note that on some FPGA boards button 0 is used as system reset. For such boards you can either do the whole lab only using button 1 or resynthesize the system, connecting reset to a button or a switch other than button 0.

3.9. Prepare the first software run

Following the procedure described in Lab YP1, compile and link the program, generate Motorola S-Record file and upload this file into the memory of the synthesized MIPSfpga-based system on the board.

Under Windows:

  1. cd programs\lab_yp3
  2. run 02_compile_and_link.bat
  3. run 08_generate_motorola_s_record_file.bat
  4. run 11_check_which_com_port_is_used.bat
  5. edit 12_upload_to_the_board_using_uart.bat based on the result from the previous step - set the working port in “set a=” assignment.
  6. Make sure the switches 0 and 1 on FPGA board are turned off and switch 2 is turned on. Switches 0 and 1 control the speed of the clock, while switch 2 determines whether the program uses interrupts (switch 2 is off) or does not use interrupts (switch 2 is on). See 3.8. Review the lab program code. If the switches 0 and 1 are not off, the loading through UART is not going to work.
  7. run 12_upload_to_the_board_using_uart.bat

Under Linux:

If uploading program to the board first time during the current Linux session, add the current user to dialout Linux group. Enter the root password when prompted:

sudo adduser $USER dialout
su - $USER

After that:

  1. cd programs/lab_yp3
  2. run ./02_compile_and_link.sh
  3. run ./08_generate_motorola_s_record_file.sh
  4. run ./11_check_which_com_port_is_used.sh
  5. edit ./12_upload_to_the_board_using_uart.sh based on the result from the previous step - set the working port in “set a=” assignment
  6. Make sure the switches 0 and 1 on FPGA board are turned off and switch 2 is turned on. Switches 0 and 1 control the speed of the clock, while switch 2 determines whether the program uses interrupts (switch 2 is off) or does not use interrupts (switch 2 is on). See 3.8. Review the lab program code. If the switches 0 and 1 are not off, the loading through UART is not going to work.
  7. ./run 12_upload_to_the_board_using_uart.sh

3.10. The first run

  1. Set the switches 0 and 1 on FPGA board to off position and switch 2 to on position. Make sure the switches 0 and 1 are off, otherwise the boot sequence (a sequence of processor instructions before main function) will take too long, since these switches control the clock frequency.
  2. Reset the processor. The reset buttons for each board are listed in the table below:

    Board Reset button
    Digilent Basys3Up
    Digilent Nexys4Dedicated CPU Reset
    Digilent Nexys4 DDRDedicated CPU Reset
    Terasic DE0Button/Key 0
    Terasic DE0-CVDedicated reset button
    Terasic DE0-NanoButton/Key 0
    Terasic DE1Button/Key 0
    Terasic DE2-115Button/Key 0
    Terasic DE10-LiteButton/Key 0

  3. Notice the output on seven-segment display. Press button 1, observe how it resets the counter value.
  4. Turn the switch 1 on. This will switch the system clock from 25 MHz to 12.5 Hz / beats per second. You should see LED 7 start blinking, it is connected straight to the system clock. Notice the speed of counting.
  5. Press button 1, observe how it resets the counter value.
  6. </li>

3.12. Review the material that explains the interrupt function attribute

Use the following internet material to make sense of interrupt-specific function attributes in the code: Using the GNU Compiler Collection (GCC). 6.31.18 MIPS Function Attributes ( http://gcc.gnu.org/onlinedocs/gcc/MIPS-Function-Attributes.html).

3.13. Review C macros that are used to access Coprocessor 0 registers

Search for mips/cpu.h header file in Codescape GCC compiler package. This file contains a set of macro definitions like mips32_getcr and mips32_bicsr that aid in accessing Coprocessor 0 registers. These registers are needed to setup the interrupts.

3.14. Review Status and Cause Coprocessor 0 register descriptions

The following materials are useful for the review:

  • Book See MIPS Run, Second Edition, by Dominic Sweetman, Chapter 3. Coprocessor 0: MIPS Process Control
  • MIPS32® microAptiv™ UP Processor Core Family Software User's Manual, Chapter 6: CP0 Registers of the microAptiv™ UP Core
  • MIPS® Architecture For Programmers Volume III: The MIPS32® and microMIPS32™ Privileged Resource Architecture

3.15. Review the portion of the lab program code that uses interrupts

The interrupt handler in programs/03_interrupts/main.c:

Setting up the Coprocessor 0 registers for handling the interrupts in programs/03_interrupts/main.c:

Code to setup the exception vector in programs/03_interrupts/exceptions.S:

The linker script which includes a new section for the exception vector in programs/03_interrupts/program.ld:

3.16. Running the program that uses interrupts

Re-run the step 3.10. The first run with switch 2 in "off" position. Do you notice the difference in program performance? Can you explain the difference?

4. Homework: doing everything in assembly

This section guides the student through the activity of analyzing the interrupt processing on assembly level. This activity helps to have a clear picture on how everything works.

4.1. Disassemble the ELF file created during the lab

Under Windows:

cd programs\lab_yp3

Under Linux:

cd programs/lab_yp3

The generated file is called program.dis. It is located in the current directory.

4.2. Review and explain the interrupt-related sections of the code

Analyze the following sections of programs/03_interrupts/program.dis file:

The code for the interrupt vector:

The code for the interrupt handler:

The code for setting up the interrupts:

The code that guards incrementing the counter in the main loop against interrupts. Such guarding is accomplished using di/ei instruction pair (disable interrupts / enable interrupts). Can you explain why is it necessary? What wrong could happen if these instructions are missing? Can you guess a scenario when such guarding is not going to work?

4.3. Rewrite the whole lab in assembly. Can you hand-write an interrupts service routine in assembly that has fewer instructions and works faster than the routine generated by GNU C compiler?

5. Follow-up projects and exercises

The topic of interrupts is an important one, and we recommend additional exercises to understand it fully.

5.1 Exercise: Observe the effect of not disabling the interrupts around critical section that updates the same variable as the interrupt handler

Comment out the assembly commands that enable and disable interrupts (asm ("di"); and asm ("ei")) around incrementing the counter variable n in main function inside main.c file:

   // asm ("di");
   n ++;
   // asm ("ei");

Build and re-run the program, using both fast and slow clocks. Can you see the difference in system responsiveness to the interrupts? You may observe cases when the system takes the interrupt and executes the interrupt service routine that resets the counter. However after returning from the interrupt, the counter appears not to be reset. Instead it continues to increment its old value, set before entering the interrupt service routine. Can you explain what is going on? We recommend to analyze the assembly output of gcc compiler by running 01_compile_c_to_assembly.sh under Linux or 01_compile_c_to_assembly.bat under Windows.

5.2 Exercise: Synchronize the updates of a counter variable using LL/SC (Load-Linked / Store-Conditional) pair of instructions

MIPS architecture provides a way of synchronizing variable updates without disabling interrupts, using a special pair of instructions LL/SC (Load-Linked / Store-Conditional). Create a version of main.c that uses this feature of the processor instead of DI/EI pair of instructions used in this lab and Exercise 5.1. If you are not familiar with LL/SC, you can review the following materials:

  1. MIPS32® microAptiv™ UP Processor Core Family Software User's Manual, section 12.3, the descriptions of LL and SC instructions. This manual is included in MIPSfpga package
  2. Book See MIPS Run, Second Edition, by Dominic Sweetman, sections 5.8.4 Critical Regions with Interrupts Enabled: Semaphores the MIPS Way and 8.5.2 Load-Linked / Store-Conditional.

You can implement this exercise using either a function written in assembly and called from main, or, alternatively, using asm construct in C with parameters, as described in online GCC documentation (https://gcc.gnu.org/onlinedocs), see 6.45.2 Extended Asm - Assembler Instructions with C Expression Operands

5.3 Investigation: Why do we need volatile qualifier for the counter variable?

Review the code of main.c. Why do we need counter variable n to be declared with volatile qualifier? How does the absence of this qualifier change the result of program execution? Try to run the program without volatile with different levels of compiler optimizations. Is there any change in behavior? Review the code produced by gcc compiler with different -O settings.

5.4 Student project and investigation: Non-maskable-interrupt (NMI) lab

Study the information about non-maskable-interrupts (NMI) in MIPS32® microAptiv™ UP Processor Core Family Software User's Manual and MIPS32® microAptiv™ UP Integrator's Guide, included in MIPSfpga package.

Create a lab that demonstrates NMI interrupts. Investigate using the internet the history of NMI in different computers. Why was this feature necessary in the past? How useful is this feature for the modern applications?

5.5 Student project: Timer interrupt lab using Count/Compare pair of Coprocessor 0 registers present inside MIPS microAptiv UP core

The timer interrupt is a special kind of hardware interrupt that occurs regularly, with a set frequency, usually in kilohertz range. Such interrupts are used to measure time intervals and to implement software multitasking, including parallel task execution in operating systems.

Read both hardware and software documentation about an embedded timer interrupt used in MIPS cores in conjunction with the Count/Compare pair of Coprocessor 0 registers. Is this feature present in MIPSfpga? If yes, implement a lab that uses this feature. This lab may for example measure time between pressing some button, or do some computation while the interrupt service routine polls some input.

5.6 Student project: Timer interrupt lab using a custom timer module implemented outside MIPS microAptiv UP core

Create an alternative implementation of the timer interrupt lab (Exercise 5.5), without using the timer interrupt already implemented in MIPS microAptiv UP core. In order to create such implementation, write a custom timer interrupt-generating module in Verilog. Then connect it to one of hardware interrupt pins, bits of SI_Int external signal.

5.7 Student project: Programmable timer interrupt lab using a custom timer module implemented outside MIPS microAptiv UP core

Extend the student project 5.6 by interfacing the timer interrupt-generating module not only to system clock, reset and SI_Int signal, but also to the system's AHB-Lite bus. The goal is that the frequency of interrupts can be controlled by the software running on MIPSfpga CPU core. For the details of AHB-Lite interfacing, see Lab YP2 - Integrating a peripheral: light sensor example.

5.8 Student project: A variant of programmable timer interrupt lab that uses multiple counters and interrupt pins

Create a version of the student project 5.7 that uses multiple counters to generate hardware interrupts with different frequences on different hardware pins (bits of the signal SI_Int).

5.9 Student exercise: Improve the interrupt lab by exposing program counter (PC) to the outside seven-segment display

Create a variant of the interrupt lab that connects the processor's program counter (PC) to the outside multiple-digit seven-segment display. This connection should be muxed with the regular seven-segment display output and should be dependent on whether some selected switch or button is pressed. With this setup, when the system clock is turned into slow mode, it will be possible to observe how the processor enters interrupt service routune.

5.10 Student investigation: Which Coprocessor 0 register bits should be exposed to outside LEDs to observe interrupts in action in slow-clock mode?

Find on the Imagination Technologies website http://imgtec.com a manual called MIPS® Architecture For Programmers Volume III: The MIPS32® and microMIPS32™ Privileged Resource Architecture. Investigate which Coprocessor 0 register fields are interesting to observe when the processor core takes an interrupt and enters the interrupt service routine in slow-clock mode. An example of such field is the EXL (Exception Level) bit of the Coprocessor 0 Status register.

Modify the core and system RTL to connect these Coprocessor 0 registers to external LEDs on FPGA board. Some fields, like Status.EXL do not require changes in core RTL because the original core RTL already outputs its value to the external pin, SI_EXL. Exposing such fields requires only changes in the system RTL (system_rtl/mfp_*.v files) . Other fields may require adding additional ports and connections to the core's RTL (core_rtl/m14k_*.v files) as well.

Document your changes and create a post on MIPSfpga forum of the Imagination Technologies website.

5.11 Advanced student project: Evaluating the usefulness and applications of Vectored Interrupt (VI) mode in MIPS architecture

MIPS microAptiv UP core, used in MIPSfpga, supports three interrupt modes: Interrupt Compatibility mode, Vectored Interrupt (VI) mode, and External Interrupt Controller (EIC) mode. So far, the lab and exercises 5.1-5.10 were using the Interrupt Compatibility mode. An alternative Vectored Interrupt (VI) mode adds the ability to prioritize and vector interrupts to a handler dedicated to that interrupt. Vectored Interrupt mode also allows assigning a GPR (General Purpose Register) shadow set for use during interrupt processing.

Study the documentation and create a lab demonstrating interrupts in Vectored Interrupt mode. How many clock cycles does this mode save when an interrupt is taken? When does it make sense to use this mode? Consider very low-power / low-frequency applications, interrupt response time, usage of shadow GPR registers (not present in MIPSfpga).

Which interrupt mode is used in Linux? Can you offer an explanation why?

5.12 Investigation: Read about IV bit of Cause Coprocessor 0 register. Why would anybody need such interrupt option? What problem is solved by this option? Propose possible answers.

5.13 Investigation: Read about EBase Coprocessor 0 register and try to explain its usefulness. It is only for multiprocessor systems? How would you use it in a single-processor system?

5.14 Advanced student project: Building External Interrupt Controller (EIC)

The External Interrupt Controller (EIC) mode is another interrupt mode, supported by MIPS microAptiv UP core used in MIPSfpga. EIC mode redefines the way interrupts are handled to provide full support for an external interrupt controller that handles prioritization and vectoring of interrupts. EIC mode is used in MIPS-based microcontrollers from Microchip Technology, including some microcontrollers from the Microchip PIC32MZ family that are built around MIPS microAptiv UP core, the same core used in MIPSfpga.

The functionality of External Interrupt Controller created by Microchip is described in Microchip software documentation, the courses taught by Microchip during Microchip Master Conference, as well as in Imagination, Microchip and Digilent-sponsored course Connected MCU created by Dr. Alexander Dean of North Carolina State University.

MIPSfpga allows you to create an alternative External Interrupt Controller, with its own interrupt scheduling and prioritization, and compare your solution to the solution from Microchip Technology. This is a significant research project that can be combined with studies on Real-Time Operating Systems (RTOS).

5.15 Medium-to-advanced student project: Create a lab demonstrating the multitasking / context switching

An important application of timer interrupts is to facilitate a variant of parallel programming called task switching, or context switching. Context switching is widely used in operating systems, from relatively simple, like FreeRTOS, to complicated, like Linux.

The idea of context switching is to periodically switch between different threads of execution, that are also sometimes called processes or tasks. The "context" is a reference to a set of information associated with the task, including program counter (PC) and general-purpose registers (GPR). The switch happens inside the timer interrupt service routine that saves the current context in some memory structure (called in some systems Process Control Block - PCB), then restores the context of a different process from another PCB and exits the timer interrupt into the new thread of execution.

Create a lab that switches between two different C functions, running in parallel. You don't need to use any operating system to do this. Just save and restore all the necessary registers inside the timer interrupt service routine, and maintain an illusion of parallel execution of two C programs for the end-user. The end-user would observe the outputs from two programs on LEDs or other output devices.

5.16 Advanced student project: Port some open-source RTOS, like FreeRTOS, to MIPSfpga

FreeRTOS is a popular real-time operating system, used as an example RTOS in the course Connected MCU created by Dr.Alexander Dean of North Carolina State University. The Connected MCU course demonstrates FreeRTOS on Microchip PIC32MZ microcontroller that uses a processor core common with MIPSfpga. However PIC32MZ uses External Interrupt Controller which is absent in the default MIPSfpga system which uses Interrupt Compatibility Mode. Porting FreeRTOS on MIPSfpga should be an appropriate project for a graduate student of embedded programming or computer architecture.

5.17 Advanced student project: Create a lab demonstrating the effect of the interrupts on processor pipelining

Connect the pipeline control signals to external LEDs on FPGA board and demonstrate how an interrupt flushes the processor pipeline from the instructions which are not going to graduate because of the interrupt. Which stage of the pipeline is used to take a pending interrupt?

If you need more information about CPU pipelining in MIPSfpga, you can review MIPSfpga 2.0. Lab YP5 - The first glance into pipelining.

5.18 Lab enhancement: Modify the system testbench to study interrupts using Verilog simulation, without synthesizing the design. Create all the necessary scripts to show the main interrupt-related signals on the waveform. Show on the waveform how an interrupt flushes the processor pipeline (see the project 5.17).

Appendix A. A list of recommended materials to review before and during the interrupt lab

  • Book See MIPS Run, Second Edition, by Dominic Sweetman. Chapter 5. Exceptions, Interrupts, and Initialization, sections 5.1-5.8. If you don't have this book, see Appendix B in this lab for critical excerpts from Sweetman's book. The specific book sections to study:

    • Chapter 5. Exceptions, Interrupts, and Initialization, sections 5.1-5.8

    • Chapter 3. Coprocessor 0: MIPS Process Control, sections:

      • 3.3.1 Status Register (SR)
      • 3.3.2 Cause Register
      • 3.3.3 Exception Restart Address (EPC) Register
      • 3.3.5 Count/Compare Registers: The On-CPU Timer
      • 3.3.8 EBase and IntCtl: Interrupt and Exception Setup
      • 3.3.9 SRSCtl and SRSMap: Shadow Register Setup
      • 3.3.10 Load-Linked Address (LLAddr) Register
    • Chapter 8. Complete Guide to the MIPS Instruction Set, section 8.5.2 Load-Linked / Store-Conditional

  • MIPS32® microAptiv™ UP Integrator's Guide, Chapter 4: Interrupt Interface. This manual is included in MIPSfpga package.

  • MIPS32® microAptiv™ UP Processor Core Family Software User's Manual, relevant sections from:

    • Chapter 5: Exceptions and Interrupts in the microAptiv™ UP Core
    • Chapter 6: CP0 Registers of the microAptiv™ UP Core
    • section 12.3, the descriptions of LL and SC instructions

    This manual is included in MIPSfpga package.

  • Relevant sections from the manual MIPS® Architecture For Programmers Volume III: The MIPS32® and microMIPS32™ Privileged Resource Architecture. This manual can be downloaded from Imagination Technologies website http://imgtec.com

Appendix B. Excerpts from a book See MIPS Run, Second Edition, by Dominic Sweetman about the interrupt processing.

Chapter 5. Exceptions, Interrupts, and Initialization

In the MIPS architecture interrupts, traps, system calls, and everything else that can disrupt the normal flow of execution are called exceptions and are handled by a single mechanism. What sort of events are they?

  • External events: Some event outside the CPU core—that is, from some real “wire” input signal. These are interrupts. (Note: There are some more obscure noninterrupt external events like bus errors reported on a read—for now, just assume that they are a special sort of interrupt). Interrupts are used to direct the attention of the CPU to some external event: an essential feature of an OS that attends to more than one different event at a time. Interrupts are the only exception conditions that arise from something independent of the CPU’s normal instruction stream. Since you can’t avoid interrupts just by being careful, there have to be software mechanisms to inhibit the effect of interrupts when necessary.

  • Memory translation exceptions: These happen when an address needs to be translated, but no valid translation is available to the hardware or perhaps on a write to a write-protected page.

    The OS must decide whether such an exception is an error or not. If the exception is a symptom of an application program stepping outside its permitted address space, it will be fixed by terminating the application to protect the rest of the system. The more common benign memory translation exceptions can be used to initiate operating system functions as complex as a complete demand-paged virtual memory system or as simple as extending the space available for a stack.

  • Other unusual program conditions for the kernel to fix: Notable among these are conditions resulting from floating-point instructions, where the hardware is unable to cope with some difficult and rare combination of operation and operands and is seeking the services of a software emulator. This category is fuzzy, since different kernels have different ideas about what they’re willing to fix. An unaligned load may be an error on one system and something to be handled in software on another.

  • Program or hardware-detected errors: This includes nonexistent instructions, instructions that are illegal at user-privilege level, coprocessor instructions executed with the appropriate SR flag disabled, integer overflow, address alignment errors, and accesses outside kuseg in user mode.

  • Data integrity problems: Many MIPS CPUs continually check data on the bus or data coming from the cache for a per-byte parity or for word- wide error-correcting code. Cache or parity errors generate an exception in CPUs that support data checking.

  • System calls and traps: These are instructions whose whole purpose is to generate recognizable exceptions; they are used to build software facilities in a secure way (system calls, conditional traps planted by careful code, and breakpoints).

5.3 Exception Vectors: Where Exception Handling Starts

. . . . . . . . . .

Here’s what a MIPS CPU does when it decides to take an exception:

  1. It sets up EPC to point to the restart location.
  2. It sets SR(EXL), which forces the CPU into kernel (high-privilege) mode and disables interrupts.
  3. Cause is set up so that software can see the reason for the exception. On address exceptions, BadVAddr is also set. Memory management system exceptions set up some of the MMU registers too; more details are given in Chapter 6.
  4. The CPU then starts fetching instructions from the exception entry point, and everything else is up to software.

. . . . . . . . . .

5.5 Returning from an Exception

The return of control to the exception victim and the change (if required) back from kernel to a lower-privilege level must be done at the same time (“atomically,” in the jargon of computer science). It would be a security hole if you ran even one instruction of application code at kernel-privilege level; on the other hand, the attempt to run a kernel instruction with user privileges would lead to a fatal exception.

MIPS CPUs have an instruction, eret, that does the whole job; it both clears the SR(EXL) bit and returns control to the address stored in EPC.

5.8.1 Interrupt Resources in MIPS CPUs

MIPS CPUs have a set of eight independent (Note: Not so independent if you’re using EIC mode; see section 5.8.5.) interrupt bits in their Cause register. On most CPUs you’ll find five or six of these are signals from external logic into the CPU, while two of them are purely software accessible. The on-chip counter/timer (made of the Count and Compare registers, described in section 3.3.5) will be wired to one of them; it’s sometimes possible to share the counter/timer interrupt with an external device, but rarely a good idea to do so.

An active level on any input signal is sensed in each cycle and will cause an exception if enabled.

The CPU’s willingness to respond to an interrupt is affected by bits in SR.

There are three relevant fields:

  • The global interrupt enable bit SR(IE) must be set to 1, or no interrupt will be serviced.
  • The SR(EXL) (exception level) and SR(ERL) (error level) bits will inhibit interrupts if set (as one of them will be immediately after any exception).
  • The status register also has eight individual interrupt mask bits SR(IM), one for each interrupt bit in Cause. Each SR(IM) bit should be set to 1 to enable the corresponding interrupt so that programs can determine exactly which interrupts can happen and which cannot.

To discover which interrupt inputs are currently active, you look inside the Cause register. Note that these are exactly that—current levels—and do not necessarily correspond to the signal pattern that caused the interrupt exception in the first place. The active input levels in Cause(IP) and the masks in SR(IM) are helpfully aligned to the same bit positions, in case you want to “and” them together. The software interrupts are at the lowest positions, and the hardware interrupts are arranged in increasing order.

In architectural terms, all interrupts are equal. (Note: That’s not quite true in vectored interrupt and “EIC mode,” described in section 5.8.5, but they’re not widely used). When an interrupt exception is taken, an older CPU uses the “general” exception entry point—though MIPS 32/64 CPUs and some other modern CPUs offer an optional distinct exception entry point reserved for interrupts, which can save a few cycles. You can select this with the Cause(IV) register bit.

Interrupt processing proper begins after you have received an exception and discovered from Cause(ExcCode) that it was a hardware interrupt. Consulting Cause(IP), we can find which interrupt is active and thus which device is signaling us. Here is the usual sequence:

  • Consult the Cause register IP field and logically “and” it with the current interrupt masks in SR(IM) to obtain a bit map of active, enabled interrupt requests. There may be more than one, any of which would have caused the interrupt.

  • Select one active, enabled interrupt for attention. Most OSs assign the different inputs to fixed priorities and deal with the highest priority first, but it is all decided by the software.

  • You need to save the old interrupt mask bits in SR(IM), but you probably already saved the whole SR register in the main exception routine.

  • Change SR(IM) to ensure that the current interrupt and all interrupts your software regards as being of equal or lesser priority are inhibited.

  • If you haven’t already done it in the main exception routine, save the state (user registers, etc.) required for nested exception processing.

  • Now change your CPU state to that appropriate to the higher-level part of the interrupt handler, where typically some nested interrupts and exceptions are permitted.

    In all cases, set the global interrupt enable bit SR(IE) to allow higher priority interrupts to be processed. You’ll also need to change the CPU privilege-level field SR(KSU) to keep the CPU in kernel mode as you clear exception level and, of course, clear SR(EXL) itself to leave exception mode and expose the changes made in the status register.

  • Call your interrupt routine.

  • On return you’ll need to disable interrupts again so you can restore the preinterrupt values of registers and resume execution of the interrupted task. To do that you’ll set SR(EXL). But in practice you’re likely to do this implicitly when you restore the just-after-exception value of the whole SR register, before getting into your end-of-exception sequence.

When making changes to SR, you need to be careful about changes whose effect is delayed due to the operation of the pipeline — “CP0 hazards.” See section 3.4 for more details and how to program around the hazards.

Appendix C. Excerpts from the article Using the GNU Compiler Collection (GCC). 6.31.18 MIPS Function Attributes


These function attributes are supported by the MIPS back end:


Use this attribute to indicate that the specified function is an interrupt handler. The compiler generates function entry and exit sequences suitable for use in an interrupt handler when this attribute is present. An optional argument is supported for the interrupt attribute which allows the interrupt mode to be described. By default GCC assumes the external interrupt controller (EIC) mode is in use, this can be explicitly set using eic. When interrupts are non-masked then the requested Interrupt Priority Level (IPL) is copied to the current IPL which has the effect of only enabling higher priority interrupts. To use vectored interrupt mode use the argument vector=[sw0|sw1|hw0|hw1|hw2|hw3|hw4|hw5], this will change the behavior of the non-masked interrupt support and GCC will arrange to mask all interrupts from sw0 up to and including the specified interrupt vector.

You can use the following attributes to modify the behavior of an interrupt handler:


Assume that the handler uses a shadow register set, instead of the main general-purpose registers. An optional argument intstack is supported to indicate that the shadow register set contains a valid stack pointer.


Keep interrupts masked for the whole function. Without this attribute, GCC tries to reenable interrupts for as much of the function as it can.


Return using the deret instruction. Interrupt handlers that don't have this attribute return using eret instead.

You can use any combination of these attributes, as shown below:

          void __attribute__ ((interrupt)) v0 ();

          void __attribute__ ((interrupt, use_shadow_register_set)) v1 ();

          void __attribute__ ((interrupt, keep_interrupts_masked)) v2 ();

          void __attribute__ ((interrupt, use_debug_exception_return)) v3 ();

          void __attribute__ ((interrupt, use_shadow_register_set,
                               keep_interrupts_masked)) v4 ();

          void __attribute__ ((interrupt, use_shadow_register_set,
                               use_debug_exception_return)) v5 ();

          void __attribute__ ((interrupt, keep_interrupts_masked,
                               use_debug_exception_return)) v6 ();

          void __attribute__ ((interrupt, use_shadow_register_set,
                               use_debug_exception_return)) v7 ();

          void __attribute__ ((interrupt("eic"))) v8 ();

          void __attribute__ ((interrupt("vector=hw3"))) v9 ();

Английский язык - это предмет гордости многих ЖЖ-юзеров, например radulova, macos, agent_marge (Эльвира Барякина) и другие титаны. По этому поводу опрос:

Кто круче пишет на современном английском - Панчул, Шекспир или Барякина?

Я (список ошибок Панчула в комментариях)
Из-за бугра плюете?

From: rezkiy
2017-01-24 06:11 am (UTC)
Статья классная, но что-то с артиклями беда совсем. Такое ощущение, что если все А поменять на the, a все the убрать, то количество ошибок уменьшится.
(Reply) (Thread)
From: rezkiy
2017-01-24 06:14 am (UTC)
я бы порекомендовал поймать близжайшего english/journalism major и прогнать статью через них.

ПО существу опроса, на английском лучше пишет Панчул, а кто круче -- не знаю.
(Reply) (Parent) (Thread)
From: u_100
2017-01-24 07:46 am (UTC)
Все english major пишут грамотно, и имеют оценки А+ по английскому?
(Reply) (Parent) (Thread)
[User Picture]From: panchul
2017-01-24 06:54 am (UTC)
А вы можете выписать список всех замеченных бед с артиклями?
(Reply) (Parent) (Thread)
From: rezkiy
2017-01-24 07:50 am (UTC)
Могу, некоторых. Я кстати не уверен в том, что мое ощущение 100% правильное.

кинул сообщение со ссылкой.

Мне кажется, основной фокус должен быть на то, чтобы студент въехал. Я хорошо знаю, что такое прерывание. И мне кажется, что объяснить надо получше. Где-то поподробнее, где-то с картинками. Фраза "The most important information is the description of interrupt pin signal SI_Int used in the lab. " как мне кажется, ключевая во всем рассказе, но ее очень трудно заметить.
(Reply) (Parent) (Thread)
[User Picture]From: Валерий Казанцев
2017-01-24 03:44 pm (UTC)
Тоже ничего нет про точные/неточные прерывания (precise/imprecise)
(Reply) (Thread)
[User Picture]From: panchul
2017-01-24 08:00 pm (UTC)
О, это хорошее замечание, спасибо
(Reply) (Parent) (Thread)
[User Picture]From: akhceloo
2017-01-25 09:29 am (UTC)
Мне почему-то достаточно тяжело читать этот текст на английском (ещё раз подтверждение, что на русском было бы понятнее).
В своё время достаточно много писала на ассемблере - начиная с ассемблера PDP-11/ДВК, Intel-x86, заканчивая AVR-ками.
Что такое прерывание с точки зрения обработчика прерывания на ассемблере, понимаю хорошо.
Хотела было написать, что нет про отличие NMI и маскируемых прерываний, но потом нашла про NMI в тексте, и поняла, что читать нужно медленнее и внимательнее.

А вот с точки зрения разработчика процессора, со стороны Verilog/VHDL или схемотехники - не продумывала подробно.
Мне казалось, что прерывание - это ведь простейшая вещь: один бит - триггер - взводит прерывание, ещё есть не изменяемое содержимое (константа) - адрес вектора прерывания в пространстве ОЗУ процессора (откуда процессор будет брать адрес перехода на обработчик - эту ячейку ОЗУ обязан заполнить программист и указать адрес своего обработчика).
В случае маскируемого прерывания есть ещё один бит - маска - разрешение/запрет прерывания.
Кажется мне, что в виде принципиальной схемы на триггерах я бы это изобразила, ничего сложного на аппаратном уровне не должно быть.

Я примерно правильно себе представляю?

Помню, что первый раз читала на русском в замечательной книжке "Майкл Сингер. Программирование на языке ассемблера для PDP-11", тогда поняла без проблем.
Тут видимо плохое сочетание: мне английский не родной.
Из приведённого выше текста пока мало что поняла, нужно читать несколько раз, пока не вникла. Простите.

Я пока даже не могу вникнуть, где у вас примеры обработчиков на С, а где Verilog код, если такой есть.

Мне кажется, для совсем начинающих нужно "разжёвывать" подробнее, сказать не об истории компьютеров, а о том, что это за зверь такой - "прерывание", причём "на пальцах" рассказать:
1) зачем оно нужно;
2) что происходит на уровне железа процессора или компьютера когда прерывание произошло;
3) что должна делать программа-обработчик прерывания которую напишет программист и почему она должна это делать;
4) что происходит на уровне железа процессора, когда выполняется команда RTI (возврат из прерывания).
Вот, по этому плану, у меня получилось бы где-то так (по английски, сорри, не могу):

1! - Зачем оно нужно? (чтобы не пропустить событие, когда твоя программа не опрашивает регистры статуса внешних событий. Можно ведь работать вообще без прерываний, но тогда твоя программа обязана сама иметь короткий по времени основной цикл, в течение которого она в том числе занимается опросом внешних регистров, иначе пропустишь событие).

2! - Что происходит в процессоре после того, как прерывание произошло? (сохраняется прежнее значение PC - Program Counter и StackPointer ++) - и обработчик, при выходе, берёт из стека прежний PC.

3! - Что должен сделать программист в процедуре обработчика? После того, как процессор перешёл на новый адрес, где должен стоять обработчик? (сохранить в стек прежние значения ВСЕХ регистров, которые он в дальнешем использует в процедуре/подбпрограмме обработчика прерывания - чтобы потом вернуть прежнее значения до выполнения команды возврата из прерывания, ассемблерная команда RTI Return From Interrupt (или как там для MIPS ассемблера условное обозначение этой команды?). Почему и для чего процедура обработки прерывания начинается с сохранения ряда регистров в стек и заканчивается извлечением этих же регистров из стека.

4! Что происходит на уровне железа при выполнении команды возврата из прерывания: (из стека прежнее значение PC - в PC, --SP

- и что ещё, что я забыла?
Сообразила: про PSW, Processor Status Word прежнее значение ничего не сказала. В стек и из стека при выходе.
(Все термины не из MIPS, а из PDP ещё в памяти у меня - сорри, если названия условные не совсем те).

Самое главное: я знаю по себе, что очень трудно, когда уже всё знаешь, сообразить, что может быть неправильно понято (misunderstand) человеком, который пока ещё ничего об этом не знает, и где нужно что "разжевать".
Вы всё знаете настолько хорошо, что Вам многое кажется очевидным, а для начинающего НЕ очевидно ВСЁ.

Edited at 2017-01-25 09:45 am (UTC)
(Reply) (Thread)
[User Picture]From: panchul
2017-01-25 07:43 pm (UTC)
При написании этого текста я подразумеваю, что студент про прерывания уже где-то читал, но ему нужно напомнить.

С хардверной точки зрения главный прикол, связанный с прерываниями - это их влияние на конвейерную обработку - ведь с одной стороны, прерывание отменяет выборку других инструкций после инструкции, на которой произошло прерывание, а с другой стороны, прерывание нельзя считать свершившимся, пока не выяснилось, не стоит ли флажок "прервано" на инструкциях, которые идут по конвейеру перед текущей, но до graduation еще не дошли.
(Reply) (Parent) (Thread)
From: rezkiy
2017-01-27 04:21 am (UTC)
>> про прерывания уже где-то читал

Когда я читал про прерывания в первый раз, я думал, что это такой способ вызова функций биоса и доса из кулхацкерных досовских программ. А прерываниями их называют для понтов. Как все на самом деле я узнал намного позже.
(Reply) (Parent) (Thread)
[User Picture]From: panchul
2017-01-27 04:27 pm (UTC)
21-е прерывание - наше все! В MIPS тоже есть и софтверные прерывания (запускаются устанавкой бита в Cop0 регистре), и исключения системного вызова (syscall) для вызова ОС.
(Reply) (Parent) (Thread)
[User Picture]From: Алексей Романов
2017-01-27 11:50 pm (UTC)
Прерывание на самом деле чуждая микпропроцессорному коду вещь. И если на asicах это неизбежное зло, необходимое для взаимодействия с асинхронными по отношению к процессору событиями, то на плис почти всегда можно либо обходится вообще без их использования, либо 1 прерыванием. И благодаря этому можно получать существенно более детерминированные системы
(Reply) (Parent) (Thread)
[User Picture]From: akhceloo
2017-01-25 09:54 am (UTC)

а на сладкое немного хулиганства программиста...

Ага, ну а на сладкое я бы рассказала про похожие на хакерские штучки, которые были возможны в прежних процессорах, когда стек хранился в незащищённой области ОЗУ, можно было подменить хранящиеся в стеке прежние значения PC и PSW (или как он у Вас? SR?) - и при выходе из прерывания улететь совсем не туда, где раньше были, а совсем даже в другую область, и с совсем другим статусом процессора (в том числе приоритет, и прочие режимы процессора поменять по сравнению с исходными).
Не знаю, можно ли такой фокус на MIPS провернуть, или у Вас в MIPS стек защищён от хулиганства программиста?

Да, читаю Ваш текст: защищён.
5.3 Exception Vectors: Where Exception Handling Starts
. . . . . . . . . .
It sets SR(EXL), which forces the CPU into kernel (high-privilege) mode and disables interrupts.

Ну, всё, CPU into kernel (high-privilege) mode - прощай, хулиганства.
Интересно, можно ли это "обойти"??? :)

Выходит, нужно (возможно, не в этой главе) ещё и рассказать, что это такое - kernel (high-privilege) mode, и как и что защищено в пользовательском режиме.

Простите, что влезла с комментариями, ничего вообще не зная про архитектуру MIPS.

Edited at 2017-01-25 10:02 am (UTC)
(Reply) (Thread)
[User Picture]From: panchul
2017-01-25 07:46 pm (UTC)

Re: а на сладкое немного хулиганства программиста...

*** Выходит, нужно (возможно, не в этой главе) ещё и рассказать, что это такое - kernel (high-privilege) mode, и как и что защищено в пользовательском режиме. ***

Я вначале собирался это сделать, но потом сообразил, что введение user mode в пример для лабы требует более сложного boot-кода (сейчас все работает в kernel mode), потому что тогда нужно ставить трансляцию виртуальных адресов для user mode. Но я наверное введу это в follow-up exercise, спасибо за подсказку.
(Reply) (Parent) (Thread)