The article provides several examples of how to configure and use MIPS32 Release 2 interrupts, including a detailed description of the configuration set in this case, and how to work with an external interrupt controller.
All the code described is published on github as part of the mipsfpga-plus [ L3 ] project.
It is assumed that the reader:
By writing this article I do not set a goal to comprehensively present all the features of work with interruptions for the MIPS microAptiv cores, since this would require extensive translation of documentation and writing about the same amount of comments. The goal is to show working examples of configuration and use of interrupts in three possible modes: backward compatibility, vector, and using an external controller. Therefore, in order to make the reader feel more comfortable, it is recommended to get acquainted with the following sections of the documentation:
In all examples, for clarity, the launch is described on the mipsfpga-plus system operating in the simulator. With equal success, this can also be done on hardware - the performance of the entire code has been tested on a Terasic DE10-Lite board [ L4 ].
Signal names are in italics ( SI_Int [7: 0] ), unless otherwise indicated, all signals are interface signals for the top-level module of the MIPSfpga system (m14k_top). For registers, a saturated font ( Count ) is used, the names of individual bits (fields) are given through a dot indicating the registers to which they relate ( Cause.DC ), on separate extracts from the documentation the register fields can be indicated by subscripts ( Cause IV ). For all registers x32 width is assumed, if it is not specified separately. When specifying constants in the extracts from the documentation, there is an indication of the number system before the "lattice" symbol (16 # 180, 2 # 00001).
Since In MIPSfpga, unfortunately, the option "GPR Shadow Registers" (Shadow Register Set, SRS) is not available, then the article omits the details related to general purpose shadow registers. Where this functionality is found in the diagrams, it is reflected in a gray (faded) color.
Exceptions are all events that lead to the transition of the processor in kernel mode: memory access errors, division by zero, etc., including interrupt requests from external devices. Handling all possible exceptions is an extensive topic that is not properly considered in isolation from the operating system (OS) kernel, which is clearly beyond the scope of this article. However, even if the use of the OS is not supposed (Bare Metal code), the developer should still provide for a minimal set of handlers in memory in order to learn about the occurrence of an exception and to prevent the program execution from going into βfree floatβ.
if Status.EXL = 1 then vectorOffset β 16#180 else if ExceptionType = TLBRefill then vectorOffset β 16#000 elseif (ExceptionType = Interrupt) then // if (Cause.IV = 0) then vectorOffset β 16#180 else if (Status.BEV = 1) or (IntCtl.VS = 0) then vectorOffset β 16#200 else if Config3.VEIC = 1 then VecNum β Cause.RIPL else VecNum β VIntPriorityEncoder() endif vectorOffset β 16#200 + (VecNum Γ (IntCtl.VS || 2#00000)) endif endif endif endif Cause.ExcCode β ExceptionType // , Status.EXL β 1 // , " " if Status.BEV = 1 then vectorBase β 16#BFC00200 else // EBase[31:30] = 2'b10 // kseg0 kseg1 vectorBase β EBase[31:12] || 16#000 endif // // vectorBase vectorOffset // 29 30 PC β {vectorBase[31:30], (vectorBase[29:0] + vectorOffset[29:0])}
The following algorithm is not taken into account in the above algorithm:
Full details of the exceptions can be found in [ D2 ].
Thus, even without using interrupts, the developer should ensure that exception handlers are available at the following offsets:
There are only three of them:
By default, the processor starts in compatibility mode (Compatibility). Further, it can be changed by setting the corresponding values ββof bits (fields):
A full description of the listed bits (fields) and registers to which they relate is given in the documentation, we note the impact of the totality of their values ββon the interrupt handling mode:
Before proceeding to the description of work with interruptions consider one of their most frequent sources - the system timer. It is extremely simple in its capabilities, but it is part of the processor core and can work even in the power saving mode, which means it is available in any environment.
Two registers are used to work with it:
To work with these registers, it is convenient to use the mips32_setcompare and mips32_setcount macros, which are declared in mips / cpu.h, to initialize and reset the timer, in fact, use the same code [ S0 ]:
mips32_setcompare(MIPS_TIMER_PERIOD); //set compare (TOP) value to turn on /reset timer mips32_setcount(0); //reset counter
The screenshot below shows the moments of timer initialization and reset when processing an interrupt.
The presence of the SI_TimerInt signal is not sufficient to trigger an interrupt. In order for it to be processed, it must be correctly routed at the RTL level, which depends on the current interrupt mode (Interrupt mode), which will be discussed below.
Backward compatibility mode with MIPS32 Release 1. Key features:
Those who received the first experience of development for embedded systems on relatively modern microcontrollers may experience some confusion from "only" 6 external interrupts and one common handler for everything, including the lion's share of exceptions. Here we should take into account some historicity of the MIPS architecture: it was assumed that the interrupt sources will be connected hierarchically, it was taken into account that the entry code in (exit) interrupt is common for all handlers, and since external devices are inherently slow (events rarely occur), then there is no particular loss in performance from "manual" checking of sources and priorities, and there is even some room for possible optimizations. An example of such a connection from [ L5 ] is shown below.
Even if these features are considered a βflawβ, they are easily compensated by the possibility of using an external interrupt controller connected via the corresponding interface of the MIPSfpga processor core, which will be discussed in the corresponding section.
`define MFP_USE_WORD_MEMORY //`define MFP_USE_IRQ_EIC
#define RUNTYPE COMPATIBILITY
02_compile_and_link.bat 05_generate_verilog_readmemh_file.bat 06_simulate_with_modelsim.bat
The settings set in the RTL header files lead to the following configuration of the interrupt interface of the processor core [ S3 ]:
assign SI_Offset = 17'b0; //not used assign SI_EISS = 4'b0; //not used assign SI_Int[7:4] = 4'b0; assign SI_Int[3] = uart_interrupt; assign SI_Int[2:0] = 3'b0; assign SI_EICVector = 6'b0; //not used assign SI_EICPresent = 1'b0; //no external interrupt controller assign SI_IPTI = 3'h7; //enable MIPS timer interrupt on HW5
The file exceptions.S contains the necessary interrupt vectors for operation, they are practically of the same type [ S4 ]:
.org 0x200 # set symbol offset from section beginning .weak __mips_isr_sw0 # if the symbol does not already exist, it will be created __isr_vec_sw0: la $k1, __mips_isr_sw0 # load interrupt handler (__mips_isr_sw0) addr beqz $k1, __general_exception # if it is not present then go to generic nop jr $k1 # jump to irq_sw0 nop
Exception vectors differ from interrupt vectors in that if the corresponding handler is not defined in the program, the code is looped [ S5 ]:
.org 0x0 # set symbol offset from section beginning .weak _mips_tlb_refill # if the symbol does not already exist, it will be created __tlb_refill: la $k1, _mips_tlb_refill # load exception handler (_mips_tlb_refill) addr beqz $k1, __tlb_refill # if _mips_tlb_refill doen not exist then just loop here nop jr $k1 # jump to _mips_tlb_refill. # we can use 'j _mips_tlb_refill' nop # but it works only with 1st 28 bits of addr
In the mian.c file, the timer [ S0 ] is sequentially initialized:
mips32_setcompare(MIPS_TIMER_PERIOD); //set compare (TOP) value to turn timer on mips32_setcount(0); //reset counter
Interrupt initialization [ S6 ]:
//compatibility mode, one common handler // Status.BEV 0 - place handlers in kseg0 (0x80000000) mips32_bicsr (SR_BEV); // Cause.IV, 0 - general exception handler (offset 0x180) mips32_biccr (CR_IV); // interrupt enable, HW5, SR_SINT1 - unmasked mips32_bissr (SR_IE | SR_HINT5 | SR_SINT1);
And their processing in a single handler, assuming sequential: checking whether the exception is an interrupt, determining which interrupt is needed to be processed, and the processing itself [ S7 ]:
void __attribute__ ((interrupt, keep_interrupts_masked)) _mips_general_exception () { MFP_RED_LEDS = MFP_RED_LEDS | 0x1; uint32_t cause = mips32_getcr(); //check that this is interrupt exception if((cause & CR_XMASK) == 0) { //check for timer interrupt if(cause & CR_HINT5) { MFP_RED_LEDS = MFP_RED_LEDS | 0x10; n++; mipsTimerReset(); mips32_biscr(CR_SINT1); //request for software interrupt 1 MFP_RED_LEDS = MFP_RED_LEDS & ~0x10; } //check for software interrupt 1 else if (cause & CR_SINT1) { MFP_RED_LEDS = MFP_RED_LEDS | 0x8; mips32_biccr(CR_SINT1); //clear software interrupt 1 flag MFP_RED_LEDS = MFP_RED_LEDS & ~0x8; } } MFP_RED_LEDS = MFP_RED_LEDS & ~0x1; }
In the timer interrupt (HW5), the counter is incremented, the timer is reset, and the program interrupt request flag SW1 is set [ S8 ]:
n++; mipsTimerReset(); mips32_biscr(CR_SINT1);
In interrupt SW1, only the interrupt flag is reset [ S9 ]:
mips32_biccr(CR_SINT1);
In the code of the main function, the counter is cyclically outputted to 7-segment indicators [ S10 ]:
for (;;) MFP_7_SEGMENT_HEX = n;
Interrupt vector mode.
Key Features:
#define RUNTYPE VECTOR
Setting up interrupt operation is as follows [ S11 ]:
//vector mode, multiple handlers // Status.BEV 0 - place handlers in kseg0 (0x80000000) mips32_bicsr (SR_BEV); // Cause.IV, 1 - special int vector (offset 0x200), // where 0x200 - base for other vectors mips32_biscr (CR_IV); // get IntCtl reg value uint32_t intCtl = mips32_getintctl(); // set interrupt table vector spacing (0x20 in our case) // see exceptions.S for details mips32_setintctl(intCtl | INTCTL_VS_32); // interrupt enable, HW5 and SW0,SW1 - unmasked mips32_bissr (SR_IE | SR_HINT5 | SR_SINT0 | SR_SINT1);
The program operation procedure is absolutely similar to the previously described one, except that the timer interrupt (HW5) is used to set not one but two program interrupt flags (SW0 and SW1), which will then be processed in order of priority [ S12 ]:
void __attribute__ ((interrupt, keep_interrupts_masked)) __mips_isr_hw5 () { MFP_RED_LEDS = MFP_RED_LEDS | 0x4; n++; mipsTimerReset(); mips32_biscr(CR_SINT0); //request for software interrupt 0 mips32_biscr(CR_SINT1); //request for software interrupt 1 MFP_RED_LEDS = MFP_RED_LEDS & ~0x4; }
For interrupt handling SW0, a separate vector is used [ S13 ]:
void __attribute__ ((interrupt, keep_interrupts_masked)) __mips_isr_sw0 () { MFP_RED_LEDS = MFP_RED_LEDS | 0x2; mips32_biccr(CR_SINT0); //clear software interrupt 0 flag MFP_RED_LEDS = MFP_RED_LEDS & ~0x2; }
Before proceeding to the description of work in the External Interrupt Controller mode, consider how this module interacts with the processor core and what it is like.
The controller's task is to register external interrupts and issue information on the most priority among them to the Interrupt Interface:
Accepting the next interrupt request for processing, the processor informs the controller:
The typical interaction between the controller and the processor core is shown below:
You should also consider:
assign eic_offset = 1'b1;
Main characteristics:
`ifdef MFP_USE_IRQ_EIC .EIC_input ( EIC_input ), .EIC_Offset ( SI_Offset ), .EIC_ShadowSet ( SI_EISS ), .EIC_Interrupt ( SI_Int ), .EIC_Vector ( SI_EICVector ), .EIC_Present ( SI_EICPresent ), .EIC_IAck ( SI_IAck ), .EIC_IPL ( SI_IPL ), .EIC_IVN ( SI_IVN ), .EIC_ION ( SI_ION ), `endif //MFP_USE_IRQ_EIC
The controller includes the following files:
In order to form a general idea of ββthe configuration of the controller, we list its configuration registers, their full description in [ D5 ]. For all registers except EICR , EISMSK_0 and EISMSK_1, it is assumed that the bit number corresponds to the controller's input number. So, for example, EIFR_0 [3] = 1 - means that an unhandled interrupt is waiting for input 3.
`define MFP_USE_IRQ_EIC `define MFP_USE_WORD_MEMORY
assign eic_offset = 1'b0;
02_compile_and_link.bat 05_generate_verilog_readmemh_file.bat 06_simulate_with_modelsim.bat
The settings set in the RTL header files lead to the following configuration [ S16 ]:
wire [ `EIC_CHANNELS - 1 : 0 ] EIC_input; assign EIC_input[`EIC_CHANNELS - 1:8] = {`EIC_CHANNELS - 6 {1'b0}}; assign EIC_input[7] = SI_TimerInt; assign EIC_input[6] = 1'b0; assign EIC_input[5] = uart_interrupt; assign EIC_input[4:2] = 3'b0; assign EIC_input[1] = SI_SWInt[1]; assign EIC_input[0] = SI_SWInt[0]; assign SI_IPTI = 3'h0;
The file exceptions.S is similar to the two previous examples, except for the naming of interrupts and their number. So, the latest HW63 is located at offset 0x9E0 [ S22 ]:
.org 0x9E0 .weak __mips_isr_eic63 __isr_vec_eic63: la $k1, __mips_isr_eic63 beqz $k1, __general_exception nop jr $k1 nop
Initialization of interrupts [ S24 ]:
//eic mode //unmask interrupt MFP_EIC_EIMSK_0 = (1 << IRQSW0) | (1 << IRQSW1) | (1 << IRQTIMER); MFP_EIC_EIMSK_1 = (1 << IRQ63); //enable auto clear MFP_EIC_EIACM_0 = (1 << IRQSW0) | (1 << IRQSW1) | (1 << IRQTIMER); //set interrupt on rising edge of the signal MFP_EIC_EISMSK_0 = (SMSK_RIZE << SMSKSW0) | (SMSK_RIZE << SMSKSW1) | (SMSK_RIZE << SMSKTIMER); // Status.BEV 0 - vector interrupt mode mips32_bicsr (SR_BEV); // Cause.IV, 1 - special int vector (0x200) // where 0x200 - base when Status.BEV = 0; mips32_biscr (CR_IV); // get IntCtl reg value uint32_t intCtl = mips32_getintctl(); // set interrupt table vector spacing (0x20 in our case) // see exceptions.S for details mips32_setintctl(intCtl | INTCTL_VS_32); // enable external interrupt controller // enable interrupts MFP_EIC_EICR = 0x1; mips32_bissr (SR_IE);
, (Option 2 β Explicit Vector Offset), (m14k_cpz_eicoffset_stub.v), mfp_eic_core.vh [ S15 ]
`define EIC_USE_EXPLICIT_VECTOR_OFFSET
, [ S26 ]:
ISR(IH_SW0) { MFP_RED_LEDS = MFP_RED_LEDS | 0x1; mips32_biccr(CR_SINT0); //clear software interrupt 0 flag MFP_RED_LEDS = MFP_RED_LEDS & ~0x1; }
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, keep_interrupts_masked, use_debug_exception_return)) v7 (); void __attribute__ ((interrupt("eic"))) v8 (); void __attribute__ ((interrupt("vector=hw3"))) v9 ();
:
void __attribute__ ((interrupt, keep_interrupts_masked)) __mips_isr_sw0 () { 80001544: 401b7000 mfc0 k1,c0_epc 80001548: 27bdfff0 addiu sp,sp,-16 8000154c: afbb000c sw k1,12(sp) 80001550: 401b6000 mfc0 k1,c0_status 80001554: afbb0008 sw k1,8(sp) // k1 status.exl,.erl,.um, .ie; 80001558: 7c1b2004 ins k1,zero,0x0,0x5 // status 8000155c: 409b6000 mtc0 k1,c0_status 80001560: afbe0004 sw s8,4(sp) 80001564: 03a0f025 move s8,sp ... } 80001568: 03c0e825 move sp,s8 8000156c: 8fbe0004 lw s8,4(sp) 80001570: 8fbb000c lw k1,12(sp) 80001574: 409b7000 mtc0 k1,c0_epc 80001578: 8fbb0008 lw k1,8(sp) 8000157c: 27bd0010 addiu sp,sp,16 80001580: 409b6000 mtc0 k1,c0_status 80001584: 42000018 eret
void __attribute__ ((interrupt)) __mips_isr_hw5 () { 80001588: 401a6800 mfc0 k0,c0_cause 8000158c: 401b7000 mfc0 k1,c0_epc 80001590: 27bdfff0 addiu sp,sp,-16 80001594: afbb000c sw k1,12(sp) 80001598: 401b6000 mfc0 k1,c0_status // k0 cause.ip7-ip2 8000159c: 001ad282 srl k0,k0,0xa 800015a0: afbb0008 sw k1,8(sp) // k1 cause.ip6-ip2 status.im7-im2 800015a4: 7f5b7a84 ins k1,k0,0xa,0x6 // k1 status.exl,.erl,.um; // .ie ( 1) 800015a8: 7c1b2044 ins k1,zero,0x1,0x4 // status 800015ac: 409b6000 mtc0 k1,c0_status 800015b0: afbe0004 sw s8,4(sp) 800015b4: 03a0f025 move s8,sp ... } 800015b8: 03c0e825 move sp,s8 800015bc: 8fbe0004 lw s8,4(sp) //disable interrupts (status.ie = 0) 800015c0: 41606000 di 800015c4: 000000c0 ehb 800015c8: 8fbb000c lw k1,12(sp) // epc 800015cc: 409b7000 mtc0 k1,c0_epc 800015d0: 8fbb0008 lw k1,8(sp) 800015d4: 27bd0010 addiu sp,sp,16 // status 800015d8: 409b6000 mtc0 k1,c0_status 800015dc: 42000018 eret
The author is grateful to the team of translators of the textbook by David Harris and Sarah Harris βDigital Circuit Design and Computer Architectureβ, by Imagination Technologies for the academic license for a modern processor core and personally for Yuri Panchul YuriPanchul for his work on promoting MIPSfpga.
[L1] - Digital circuit design and computer architecture ;
[L2] - How to start working with MIPSfpga ;
[L3] - MIPSfpga-plus project on github ;
[L4] β FPGA Terasic DE10-Lite ;
[L5] β Embedded Linux System Design and Development P. Raghavan, Amol Lad, Sriram Neelakandan ( );
[L6] β ahb_lite_eic github ;
[L7] β Codescape MIPS SDK ;
[D1] β MIPS32 microAptiv UP Processor Core Family Integrator's Guide ;
[D2] β MIPS32 microAptiv UP Processor Core Family Software User's Manual ;
[D3] β Codescape GNU Tools for MIPS Programmer's Guide ;
[D4] β MIPS32 Architecture For Programmers Volume III: The MIPS32 Privileged Resource Architecture ;
[D5] β MIPSfpga+ External Interrupt Controller ;
[D6] β MIPS32 microAptiv UP Processor Core Family Datasheet ;
[P0] β MIPS 32 microAptiv UP Core Block Diagram (: D6 );
[P1] β ;
[P2] β MIPS. ();
[P3] β (: L5 );
[P4] β . ();
[P5] β (: D2 );
[P6] β (: D2 );
[P7] β (: D2 );
[P8] β . ();
[P9] β (: D2 );
[P10] β (: D1 );
[P11] β (: L6 );
[P12] β , ();
[P13] β , ().
[S0] β ;
[S1] β mipsfpga-plus ;
[S2] β 06_timer_irq ;
[S3] β mipsfpga-plus ;
[S4] β ;
[S5] β ;
[S6] β ;
[S7] β ;
[S8] β ;
[S9] β ;
[S10] β main 06_timer_irq ;
[S11] β ;
[S12] β ;
[S13] β ;
[S14] β ;
[S15] β mipsfpga-plus ;
[S16] β mipsfpga-plus ;
[S17] β ;
[S18] β ;
[S19] β ;
[S20] β AHB-Lite ;
[S21] β ;
[S22] β HW63 ;
[S23] β ;
[S24] β ;
[S25] β Setting the vector offset increment in the external interrupt controller mode ;
[S26] - Interrupt handler in the external interrupt controller mode ;
[S27] - ISR and EH_GENERAL macros ;
[S28] - Module interrupt_sence ;
[S29] - Module interrupt_channel .
Source: https://habr.com/ru/post/324900/
All Articles