OMAP-L138的新历程之ARM裸机中断AINTC(5)
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
OMAP-L138的新历程之ARM裸机中断AINTC(5)
上一节我在此说了准备移植linux等操作系统,由于这些系统的移植工作在我购买开发板的时候的厂家已经做了。
所以大家想移植这些系统可以先参看厂家的移植实例,但是由于代码量太庞大了。
所以我在这儿先想把片上的各个模块测试理解一遍,这样在做系统调用的时候思路就比较清晰了!
先请大家看看omapl138 arm处理机执行中断的流程:
1.首先就是知道ARM状态下的通用寄存器和程序计数器,绿颜色的就是相应模式下的私有寄存器。
就是说程序一般运行在系统和用户模式下,使用的是系统和用户模式下的通用寄存器,当有异常发生时,比如FIQ,那么系统将切换到FIQ模式下,相应的就会采用FIQ模式下的寄存器,其中绿颜色的就是只在FIQ模式下才会用到的寄存器。
2.在模式切换的过程中,要保护系统和用户模式下的通用寄存器状态,以便在异常处理完成之后程序能正常返回。
因为FIQ模式下R8-R14为其私有寄存器,所以切换的过程中,系统和用户模式下的通用寄存器的R8-R14就不用保护了,所以减少了对寄存器存取的需要,从而可以快速的进行FIQ处理,故称为FIQ。
3. 异常处理的动作。
当然这都是内核自己干的。
以FIQ为例。
当系统进入FIQ模式时,
第一,将原来执行程序的下一条指令地址保存到LR中,就是将R14保存到R14_fiq里面。
第二,拷贝CPSR到SPSR_fiq。
第三,改变CPSR模式位的值,改到FIQ模式。
第四,改变PC值,将其指向异常处理向量所指的下一条指令。
离开异常处理的时候,
第一,将LR(R14_fiq)赋给PC。
第二,将SPSR(SPSR_fiq)拷贝到CPSR。
第三,清除中断禁止标志(如果开始时置位了)。
4.异常中断向量
异常中断的向量地址
地址异常中断类型入口时处理器的操作模式
0×00000000 复位超级用户
0×00000004 未定义指令未定义
0×00000008 软件中断超级用户
0x0000000c 中止(预取指)中止
0×00000010 中止(数据)中止
0×00000014 保留保留
0×00000018 IRQ IRQ
0x0000001c FIQ ; FIQ
异常中断优先级
中断优先级
复位最高
数据异常
FIQ
IRQ
预取指异常中断
未定义指令和软件中断最低
5.当发生IRQ中断时
第一,模式进入到IRQ里面。
第二,PC跳到0×00000018处运行。
因为这是IRQ的中断入口。
第三,通过0×00000018:LDR PC, IRQ_ADDR。
跳转到相应的中断服务程序。
这个里面就有个确定哪个中断源的问题了。
那就有优先级的问题了。
每个中断源会有自己的中断服务程序。
第四,得到中断源有硬件实现和软件处理两种方式。
omapl138的就是利用硬件方式,为了利用向量中断控制器的优点,IRQ中断向量入口处代码做了修改,变成0×00000018:LDR PC, [PC, #-0xA1C]。
这条指令从内存映射地址0xFFFEF604处获得数据装载到PC,这样就能够直接从硬件中获得中断源。
这样就减少了中断延迟。
记得,三星的S3C44B0采用的是用软件确定中断源,因此要建立中断向量表。
第五,得到中断源,就知道要跳到哪个中断服务程序去了。
一般都是这么定义的。
Timer0_Handler HANDLER Timer0 。
这种格式是调用一种宏定义,目的是保护现场,跳到中断服务程序。
好了,以上是arm的一般执行中断的方法。
我们来看看omapl138的一般实现过程。
值得注意的是:我们一般在使用中断的时候,很多人就想直接使能中断就行了,但是由于arm有几种模式,每一种模式下面都会有自己的堆栈和一些特殊的寄存器。
所以我们在开启中断之前得做一些初始化工作,比如关闭mmu,初始化各个模式的堆栈等
在这儿我做的实验程序是以timer0 32位模式为基础的,实现了一个中断过程。
首先我们得初始化aintc 这个中断管理子模块,第一步就是先关闭全局中断,然后将我们的的定时器子中断,安装我们定时器的中断向量函数表(也就是将函数地址放到我们子中断向量表中),将我们的定时的中断映射2~31中的某一个中断通道,最后在使能SYSINT_T64P0_TINT12中断,这样就建立起来了一个中断连接关系。
设置好定时器之后就可以开始我们的中断了,当定时器到一定时间之后产生一个cpu中断,然后cpu就跳转到0xffff0000那儿去执行我们的向量。
但是不知道是我的CCS的原因还是怎么回事。
我直接在cmd中将我们的arm中断向量分配到ram的local ram中,编译不能通过。
所以我只能想个办法在cmd中先把他放在公共的空间中,当程序运行的时候先搬移这个向量表到arm 的ram中,如果有高手看到我的文章有解决方法,请给我留言,谢谢!
现在我们可以进入我们的中断值函数了,由于产生中断之后IRQ会自动的关闭,所以我们在中断子函数中重新打开,不然下一个中断就无法进入,并且aintc模块也会设置标准位,这个也得我们手动清除。
我的处理方法是:将LR寄存器的值减四,因为arm是三级流水的,cpu执行的当前指令的时候LR中的值是下下条指令的地址。
然后将pc的值修改成我们的中断子程序的地址。
这样执行完中断子程序的时候让cpu 感觉是发生函数调用而已。
当然进入我们真正的中断函数之后还得清除定时器自己的中标志位。
接着下一个中断开始!
初始化mmu:
.align 4
$C$CON1: .field 00002001h,32
.align 4
$C$CON2: .field 00002078h,32 ;SBO set and Vector is 0xFFFF0000 .global _c_intOMAP
;***************************************************************
;* FUNCTION DEF: _c_intOMAP
;***************************************************************
_c_intOMAP:
;*——————————————————
; Handle MMU now
LDR R1, $C$CON1
MCR p15, #0, r0, c15, c1, #0 ;allow access to all co processors
NOP
NOP
NOP
LDR r0, $C$CON2 ; disable MMU, caches, write buffer
MCR p15, #0, r0, c1, c0, #0
NOP
NOP
NOP
mvn r0, #0
mcr p15, #0, r0, c8, c7, #0 ; flush tlb’s
mcr p15, #0, r0, c7, c7, #0 ; flush Caches
mcr p15, #0, r0, c7, c10, #4 ; flush Write Buffer
NOP
NOP
NOP
mvn r0, #0
mcr p15, #0, r0, c3, c0, #0 ; grant manager access to all domains NOP
NOP
NOP
;*——————————————————
;* Disable ITCM bus
;*——————————————————
LDR R0 , ITCM_RegionReg_Val
MCR p15, #00, R0, c9, c1, #1
NOP
NOP
;*——————————————————
;* Disable DTCM bus
;*——————————————————
LDR R0 , DTCM_RegionReg_Val
NOP
NOP
MCR p15, #00, R0, c9 , c1, #0
NOP
NOP
初始化各个模式的堆栈:
_initStack:
;*——————————————————
;* SET TO IRQ MODE
;*——————————————————
MRS r0, cpsr
BIC r0, r0, #0x1F ; CLEAR MODES
ORR r0, r0, #0×12 ; SET IRQ MODE
MSR cpsr, r0
;*——————————————————
;* INITIALIZE THE IRQ MODE STACK ;*——————————————————
LDR SP, i_stack
LDR R0, i_STACK_SIZE
ADD SP, SP, R0
;*——————————————————
;* SET TO FIQ MODE
;*——————————————————
MRS r0, cpsr
BIC r0, r0, #0x1F ; CLEAR MODES
ORR r0, r0, #0×11 ; SET FIQ mode
MSR cpsr, r0
;*——————————————————
;* INITIALIZE THE FIQ MODE STACK ;*——————————————————
LDR SP, f_stack
LDR R0, f_STACK_SIZE
ADD SP, SP, R0
;*——————————————————
;* SET TO UNDEF MODE
;*——————————————————
MRS r0, cpsr
BIC r0, r0, #0x1F ; CLEAR MODES
ORR r0, r0, #0x1B ; SET UNDEF mode
MSR cpsr, r0
;*——————————————————
;* INITIALIZE THE UNDEF MODE STACK ;*——————————————————
LDR SP, u_stack
LDR R0, u_STACK_SIZE
ADD SP, SP, R0
;*——————————————————
;* SET TO SYSTEM MODE
;*——————————————————
MRS r0, cpsr
BIC r0, r0, #0x1F ; CLEAR MODES
ORR r0, r0, #0x1F ; SET SYSTEM mode
MSR cpsr, r0
;*——————————————————
;* INITIALIZE THE SYSTEM MODE STACK ;*——————————————————
LDR SP, y_stack
LDR R0, y_STACK_SIZE
ADD SP, SP, R0
;*——————————————————
;* SET TO ABORT MODE
;*——————————————————
MRS r0, cpsr
BIC r0, r0, #0x1F ; CLEAR MODES
ORR r0, r0, #0×17 ; SET ABORT mode
MSR cpsr, r0
;*——————————————————
;* INITIALIZE THE ABORT MODE STACK ;*——————————————————
LDR SP, a_stack
LDR R0, a_STACK_SIZE
ADD SP, SP, R0
;*——————————————————
;* SET TO SUPERVISOR MODE
;*——————————————————
MRS r0, cpsr
BIC r0, r0, #0x1F ; CLEAR MODES
ORR r0, r0, #0×13 ; SET SUPERVISOR mode
MSR cpsr, r0
;*——————————————————
;* INITIALIZE THE SUPERVISOR MODE STACK ;*——————————————————
LDR SP, s_stack
LDR R0, s_STACK_SIZE
ADD SP, SP, R0
初始化arm内部寄存器:
; Initialize ARM926 internal registers.
MOV r0, #0
MOV r1, #0
MOV r2, #0
MOV r3, #0
MOV r4, #0
MOV r5, #0
MOV r6, #0
MOV r7, #0
MOV r8, #0
MOV r9, #0
MOV r10, #0
MOV r11, #0
MOV r12, #0
B _main
一串串汇编之后就进入我的c函数了
void main()
{
unsigned int i;
SetPll0(0,30,1,0,1,2,5);
SetPll1(30,1,0,1,2);
SetDdrFrequency(150);
InitAintc();
initTimer();
while(1)
{
}
}
void InitAintc()
{
volatile unsigned int *cmr;
int i;
// Disable interrupts at high level
AINTC_GER = 0×00;
// clear any pending interrupts
AINTC_SICR = IRQ_ACTIVE_INDEX;
AINTC_SICR = IRQ_ACTIVE_INDEX;
AINTC_SICR = IRQ_ACTIVE_INDEX;
AINTC_SITR1 = 0xFFFFFFFF; //Set interrupts to edge-triggered AINTC_SITR2 = 0xFFFFFFFF;
AINTC_SITR3 = 0xFFFFFFFF;
AINTC_ECR1 = 0xffffffff; // disable all system interrupts
AINTC_ECR2 = 0xffffffff;
AINTC_ECR3 = 0xffffffff;
AINTC_HIER = 0; // disable IRQ & FIQ
AINTC_SICR = ARM_TIMER_TICK_CHANNEL; // clear interrupt AINTC_EICR = ARM_TIMER_TICK_CHANNEL; // At ARM level
// Clear out channel mapping, do here in case using JTAG and restarting for (cmr = ADDR_AINTC_CMR0_22, i=0;i!=23;i++,cmr++)
{
cmr[i] = 0;
}
InitVectors();
// int_install_arm_vec_handler(UNDEFINED_INST_VEC, (PFV) undefined_abort);
// Enable interrupts at high level
AINTC_GER = 0×01;
AINTC_HIER = 0×03; // enable IRQ interrupts for host interrupt
AINTC_CR = 0×18; // Nesting mode auto
// We don’t use FIQ interrupts so leave them disables, especially while debugging
// AINTC_HIEISR = 0×00; // enable FIQ interrupts for host interrupt
portENABLE_INTERRUPTS();
}
void initTimer()
{
void resetTimer(unsigned int tmrNum);
void _irqTIMER0(void);
resetTimer(0); // half of this timer is used for rtos tick
resetTimer(1); // This timer is not used in this application
/* Install timer and software interrupt handlers */
int_install_irq_handler(ARM_TIMER_TICK_CHANNEL, (PFV)_irqTIMER0);
// Channel Mapping for timer
ARM_VECTOR_CMR_ENTRY(ARM_TIMER_TICK_CHANNEL) =
(ARM_VECTOR_CMR_ENTRY(ARM_TIMER_TICK_CHANNEL) &
ARM_VECTOR_CMR_MASK (ARM_TIMER_TICK_CHANNEL)) +
(ARM_TIMER_PRIORITY << (ARM_VECTOR_CMR_NIBBLE(ARM_TIMER_TICK_CHANNEL) *8));
AINTC_EISR = ARM_TIMER_TICK_CHANNEL; // At ARM level
// Set PERIOD registers
TMR_REGS[0]->PRD12 = 0xfffff;
// Set up DUAL 32-bit on Timer0
TMR_REGS[0]->TGCR = CSL_FMKT(TMR_TGCR_TIMMODE, 32BIT_UNCHAIN) |
CSL_FMKT(TMR_TGCR_PLUSEN, ENABLE);
TMR_REGS[0]->EMUMGT = 1; // run in emulation mode as well
TMR_REGS[0]->INTCTLSTAT = CSL_FMKT(TMR_INTCTLSTAT_PRDINTEN34, DISABLE) | CSL_FMKT(TMR_INTCTLSTAT_PRDINTSTAT34, PEND) |
CSL_FMKT(TMR_INTCTLSTAT_PRDINTEN12, ENABLE) |
CSL_FMKT(TMR_INTCTLSTAT_PRDINTSTAT12, PEND);
TMR_REGS[0]->TGCR |= CSL_FMKT(TMR_TGCR_TIM34RS, NO_RESET) |
CSL_FMKT(TMR_TGCR_TIM12RS, NO_RESET);
TMR_REGS[0]->REL12=0xfffff;
// Enable Timer for continuous operation
CSL_FINST(TMR_REGS[0]->TCR, TMR_TCR_ENAMODE12, EN_CONTRELOAD);
return;
}
中断处理函数:
__irqTIMER0:
SUB SP,SP,#4 ;//reserved for PC
STMFD SP!,{R0-R1}
LDR R1, c_hipir2 ; Host interrupt priority index register, IRQ
LDR R0, [R1] ; Get the interrupt index
STR R0, [R1] ; Write it back to HIPIR to unlock it
LDR R1, c_sicr ; Now get the system interrupt status clear register
STR R0, [R1] ; Clear the host interrupt
LDR R0,c_Timer0Handler
STR R0,[SP,#8]
SUB LR, LR, #4
MRS R0, cpsr
BIC R0, R0, #0xC0
MSR cpsr_c, R0
LDMFD SP!,{R0-R1,pc}
中断服务函数:
void Timer0Handler()
{
//initTimer();
CSL_FINST(TMR_REGS[0]->INTCTLSTAT, TMR_INTCTLSTAT_EVTINTEN12, SHIFT); }。