ILD

Nested interrupt
作者:Herbert Yuan 邮箱:yuanjp89@163.com
发布时间:2017-11-2 站点:Inside Linux Development

本文针对s3c2440 CPU,ARM920T core, ARMv4t architecture. 现代的处理器和中断控制器有很大不同,对嵌套中断和其它特性支持的更好,有机会后面再学习。


中断模式下被打断的话,则会将PC拷贝到LR,原来的LR被覆盖了。为了解决这个问题,ARMv4t增加了System模式。嵌套中断的基本套路是,处理器响应中断后,在中断模式只做一些保存工作,然后切换到系统模式处理中断。在系统模式下在打开中断。处理完后,切换回中断模式,同时关闭中断,在返回被打断的环境。


直接看代码把,头晕的很。


start.S

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
    .text
    .global _start
_start:
    b reset
    b loop
    b loop
    b loop
    b loop
    b loop
    b int
    b loop
reset:
    ldr sp, =4096
    bl init
    bl main
    b loop
int:
    sub lr,lr,#4
    push {r0-r3,r12,lr}          @ save unbanked caller-saved register and lr
    mrs lr, spsr                 @ copy spsr to lr
    push {lr}                    @ save spsr
    msr cpsr_c, #0xdF            @ enter SYSTEM mode (with IRQ / FIQ disabled)
    push {lr}                    @ save old interrupt handler's lr
    bl irq_handler               @ interrupt handler
    pop {lr}                     @ pop old interrupt handler's lr
    msr cpsr_c, #0xd2            @ enter IRQ mode (with IRQ / FIQ disabled)
    pop {lr}                     @ pop spsr to lr
    msr spsr_cxsf, lr            @ restor spsr
    ldmfd sp!, {r0-r3,r12,pc}^   @ return
loop:
    b loop


init.c

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
// watchdog
#define     WTCON           (*(volatile unsigned long *)0x53000000)
 
#define     GPFCON           (*(volatile unsigned long *)0x56000050)
#define     GPFDAT           (*(volatile unsigned char *)0x56000054)
#define     GPGCON          (*(volatile unsigned long *)0x56000060)
 
#define     EINTMSK        (*(volatile unsigned long *)0x560000a4)
#define     EINTPND        (*(volatile unsigned long *)0x560000a8)
#define     EXTINT0         (*(volatile unsigned long *)0x56000088)
#define     EXTINT1         (*(volatile unsigned long *)0x5600008c)
 
typedef unsigned long uint32_t;
typedef unsigned char uint8_t;
 
struct S3C2440_INT_CONT
{
    uint32_t SRCPND;
    uint32_t INTMOD;
    uint32_t INTMSK;
    uint32_t PRIO;
    uint32_t INTPND;
    uint32_t INTOFFSET;
    uint32_t SUBSRCPND;
    uint32_t INTSUBMSK;
};
 
volatile struct S3C2440_INT_CONT *intcont = (volatile struct S3C2440_INT_CONT *)0x4a000000;
 
void disable_watchdog()
{
    WTCON = 0; 
}
 
void init_sdram()
{
    *(volatile unsigned long *)0x48000000 = 0x22011110;  // BWSCON
    *(volatile unsigned long *)0x48000004 = 0x00000700;  // BANKCON0
    *(volatile unsigned long *)0x48000008 = 0x00000700;  // BANKCON1
    *(volatile unsigned long *)0x4800000c = 0x00000700;  // BANKCON2
    *(volatile unsigned long *)0x48000010 = 0x00000700;  // BANKCON3
    *(volatile unsigned long *)0x48000014 = 0x00000700;  // BANKCON4
    *(volatile unsigned long *)0x48000018 = 0x00000700;  // BANKCON5
    *(volatile unsigned long *)0x4800001c = 0x00018005;  // BANKCON6
    *(volatile unsigned long *)0x48000020 = 0x00018005;  // BANKCON7
    *(volatile unsigned long *)0x48000024 = 0x008C07A3;  // REFRESH
    *(volatile unsigned long *)0x48000028 = 0x000000B1;  // BANKSIZE
    *(volatile unsigned long *)0x4800002c = 0x00000030;  // MRSRB6
    *(volatile unsigned long *)0x48000030 = 0x00000030;  // MRSRB7
}
 
void init_gpio()
{
#define set_gpio(r,v,n) r &= ~(3 << 2*n); r |= (v << 2*n)
     
    unsigned long r1 = GPFCON;
    unsigned long r2 = GPGCON;
 
    // 4,5,6 LED output mode
    set_gpio(r1,1,4);
    set_gpio(r1,1,5);
    set_gpio(r1,1,6);
 
    // 0(EINT0),2(EINT2) Button INT Mode
    set_gpio(r1,2,0);
    set_gpio(r1,2,2);
 
    // 3(EINT11) BUtton INT Mode
    set_gpio(r2,2,3);
 
    GPFCON = r1;
    GPGCON = r2;
}
 
void init_interrupt_controller()
{
    int m = 0;
 
    // enable EINT11
   EINTMSK &= ~(1 << 11); 
   EXTINT0 = (m << 2*4) | (m << 0*4);
 
   intcont->INTMSK = ~ ((1<<0)|(1<<2)|(1<<5));
   EXTINT1 = (m << (11-8)*4);
}
 
void init()
{
    disable_watchdog();
     
    init_sdram();
 
    init_gpio();
 
    init_interrupt_controller();
      
    asm volatile (
        "msr cpsr_c, #0xd2\n\t"     // enter IRQ mode
        "ldr sp, =0x32000000\n\t"   // set IRQ mode stack
        "msr cpsr_c, #0xdf\n\t"     // enter system mode
        "ldr sp, =0x31000000\n\t"   // set system mode stack
        "msr cpsr_c, #0x53");       // re-enter svc mode and enable IRQ and FIQ
 
}
 
void main()
{
     int i = 0;
 
    while (1)
    {
        i++;
        if (i == 300) {
            GPFDAT = -1;
            i = 0;
        }
    
}
 
void irq_handler()
{
    unsigned long off = intcont->INTOFFSET;
    unsigned long led = 0;
    int i = 0;
    int j = 30;
     
    if (off == 0)
        led = 6;
    else if (off == 2)
        led = 5;
    else if (off == 5)
        led = 4;
 
    if (intcont->SRCPND & 1)
        GPFDAT &= ~(1 << 6);
    if (intcont->SRCPND & (1 << 2))
        GPFDAT &= ~(1 << 5);
    if (intcont->SRCPND & (1 << 5))
        GPFDAT &= ~(1 << 4);
 
    intcont->INTMSK |= (1 << off);          // mask this interrupt 
    intcont->INTPND = 1 << off;             // clear INTPND 
    asm volatile ("msr cpsr_c, #0x1F");     // Open IRQ
 
    if (led != 0)
    while (1)
    {
        i++;
        if (i == 10000) {
            GPFDAT = -1;
        }
        else if (i == 20000) {
            GPFDAT = ~(1 << led);
            j--;
            i = 0;
            if (j == 0)
                break;
        }
    }
 
    // clear int
    if (off == 5) {
        EINTPND = 1 << 11;
    }
    intcont->SRCPND = 1 << off;
    intcont->INTPND = 1 << off;
 
    // clear mask
    intcont->INTMSK &= ~(1 << off);
 
}


在中断处理中开中断之前,需要先屏蔽本中断,然后清掉INTPND,否则本中断会持续触发。(想想:如果中断被配置为电平模式,开中断后,你肯定是按一定时间,再快也有个时间,这段时间不断触发,自己打断自己,所以需要屏蔽本中断)。由于本中断被屏蔽,那就没有优先级的概念了,所有其它中断都可以打断它。


思考:

Nested interrupt 和 Re-entrant interrupt 的异同?


参考:

ARMCC: NESTING INTERRUPTS. http://www.keil.com/support/docs/3353.htm


Copyright © linuxdev.cc 2017-2024. Some Rights Reserved.