基于ARM的nucleus plus的执行

来源:互联网 发布:阿里云邮件推送 编辑:程序博客网 时间:2024/06/08 19:44


一.ARM处理器的运行模式

ARM处理器在嵌入式系统中应用广泛,共有7种运行模式:

处理器模式

描述

用户模式

正常程序执行的模式

快速中断模式(FIQ

用于告诉数据传输和通道处理

外部中断模式(IRQ

用于通常的中断处理

特权模式(Supervisor SVE

供操作系统使用的一种保护模式

数据访问中止模式(Abort

用于虚拟存储以及存储保护

未定义指令中止模式(Undefined

当未定义的指令执行时进入该模式,可用于支持硬件协处理器的软件仿真

系统模式

用于运行特权级的操作系统任务

 

各种不同异常模式下使用的寄存器组:

    运行模式位M[4:0]的具体含义

M[40]

处理器模式

可访问的寄存器

0b10000

用户模式

PCCPSR,R0-R14

0b10001

FIQ模式

PCCPSR, SPSR_fiqR14_fiq-R8_fiq, R7R0

0b10010

IRQ模式

PCCPSR, SPSR_irqR14_irq,R13_irq,R12R0

0b10011

管理模式

PCCPSR, SPSR_svcR14_svc,R13_svc,,R12R0,

0b10111

中止模式

PCCPSR, SPSR_abtR14_abt,R13_abt, R12R0,

0b11011

未定义模式

PCCPSR, SPSR_undR14_und,R13_und, R12R0,

0b11111

系统模式

PCCPSRARM v4及以上版本), R14R0

 

二.ARM处理器的异常中断

ARM指令执行的过程中,通常有三种执行的方式:

1.顺序执行,每执行一个指令,程序计数器寄存器(PC)增加;如果执行ARM指令,程序计数器寄存器(PC)的值加4,如果执行Thumb指令,PC值加两个字节;

2.执行跳转指令,如BBXBLBLX指令,程序可以跳转到特定的地址标号处执

3.异常中断发生时,程序跳转到中断程序中执行,在处理完中断以后,程序返回到发生中断的指令的下一条指令处执行,在进出中断时要保存现场。

 

ARM体系中异常中断的种类

ARM体系结构所支持的异常及具体含义如表所示。

ARM体系结构所支持的异常

异常类型

具体含义

复位(Reset

当处理器的复位电平有效时,产生复位异常,程序跳转到复位异常处理程序处执行。复位异常中断通常用在下面两种情况:系统加电和系统复位时。

未定义指令

(undefined instruction)

ARM处理器或协处理器遇到不能处理的指令时,产生未定义指令异常。可使用该异常机制进行软件仿真。

软件中断

(softwart interrupt SWI)

由用户定义,该异常由执行SWI指令产生,可用于用户模式下的程序调用特权操作指令。在实时操作系统中可使用该异常机制实现系统功能调用。

指令预取中止

(Prefech Abort)

若处理器预取指令的地址不存在,或该地址不允许当前指令访问,存储器会向处理器发出中止信号,但当预取的指令被执行时,才会产生指令预取中止异常。

数据访问中止

(Data Abort)

若处理器数据访问指令的地址不存在,或该地址不允许当前指令访问时,产生数据中止异常。

IRQ(外部中断请求)

当处理器的外部中断请求引脚有效,且CPSR中的I位为0时,产生IRQ异常。系统的外设可通过该异常请求中断服务。

FIQ(快速中断请求)

当处理器的快速中断请求引脚有效,且CPSR中的F位为0时,产生FIQ异常。

 

 

当系统进入异常时,转而执行不同向量地址的指令,如下表所示:

异常向量表

地址

异常

进入模式

0x0000,0000

复位

管理模式

0x0000,0004

未定义指令

未定义模式

0x0000,0008

软件中断

管理模式

0x0000,000C

中止(预取指令)

中止模式

0x0000,0010

中止(数据)

中止模式

0x0000,0014

保留

保留

0x0000,0018

IRQ

IRQ

0x0000,001C

FIQ

FIQ

异常优先级

当多个异常同时发生时,系统根据固定的优先级决定异常的处理次序。异常优先级由高到低的排列次序如表所示。

异常优先级

优先级

异  常

1(最高)

复位

2

数据中止

3

FIQ

4

IRQ

5

预取指令中止

6(最低)

未定义指令、SWI

 

对异常的响应

当一个异常出现以后,ARM微处理器会执行以下几步操作:

1、将下一条指令的地址存入相应连接寄存器LR,以便程序在处理异常返回时能从正确的位置重新开始执行。若异常是从ARM状态进入,LR寄存器中保存的是下一条指令的地址(当前PC+4或PC+8,与异常的类型有关);若异常是从Thumb状态进入,则在LR寄存器中保存当前PC的偏移量,这样,异常处理程序就不需要确定异常是从何种状态进入的。例如:在软件中断异常SWI,指令 MOV PC,R14_svc总是返回到下一条指令,不管SWI是在ARM状态执行,还是在Thumb状态执行。

2、将CPSR复制到相应的SPSR中。

3、根据异常类型,强制设置CPSR的运行模式位。

4、强制PC从相关的异常向量地址取下一条指令执行,从而跳转到相应的异常处理程序处。

还可以设置中断禁止位,以禁止中断发生。

如果异常发生时,处理器处于Thumb状态,则当异常向量地址加载入PC时,处理器自动切换到ARM状态。

ARM微处理器对异常的响应过程用伪码可以描述为:

R14_<Exception_Mode> = Return Link

SPSR_<Exception_Mode> = CPSR

CPSR[4:0] = Exception Mode Number

CPSR[5] = 0                 ;当运行于ARM工作状态时

If <Exception_Mode> == Reset or FIQ then

                         ;当响应FIQ异常时,禁止新的FIQ异常

  CPSR[6] = 1      

CPSR[7] = 1

    PC = Exception Vector Address

 

从异常返回

异常处理完毕之后,ARM微处理器会执行以下几步操作从异常返回:

1、将连接寄存器LR的值减去相应的偏移量后送到PC中。

2、将SPSR复制回CPSR中。

3、若在进入异常处理时设置了中断禁止位,要在此清除。

可以认为应用程序总是从复位异常处理程序开始执行的,因此复位异常处理程序不需要返回。如下表所示,表中总结了进入异常处理时保存在相应R14中的PC值,及在退出异常处理时推荐使用的指令。

表2-4  异常进入/退出

 

返回指令

以前的状态

注意

ARM  R14_x

Thumb R14_x

BL

MOV PC,R14

PC+4

PC+2

1

SWI

MOVS PC,R14_svc

PC+4

PC+2

1

UDEF

MOVS PC,R14_und

PC+4

PC+2

1

FIQ

SUBS PC,R14_fiq,#4

PC+4

PC+4

2

IRQ

SUBS PC,R14_irq,#4

PC+4

PC+4

2

PABT

SUBS PC,R14_abt,#4

PC+4

PC+4

1

DABT

SUBS PC,R14_abt,#8

PC+8

PC+8

3

RESET

NA

4

注意:

1、在此PC应是具有预取中止的BL/SWI/未定义指令所取的地址。

2、在此PC是从FIQ或IRQ取得不能执行的指令的地址。

3、在此PC是产生数据中止的加载或存储指令的地址。

4、系统复位时,保存在R14_svc中的值是不可预知的。

 

 

在应用程序中安装异常中断处理程序:

1)地址0x0处为ROM的情况,当地址0x0处为ROM时,在异常中断向量表中,可以使用数据读取指令LDR直接向程序计数器PC中赋值

INT_Vectors

LDR pc,INT_Table                  ;0x0000

LDR pc,(INT_Table + 4)            ;0x0004

LDR pc,(INT_Table + 8)            ;0x0008

LDR pc,(INT_Table + 12)           ;0x000C

LDR pc,(INT_Table + 16)           ;0x0010

LDR pc,(INT_Table + 20)           ;0x0014

LDR pc,(INT_Table + 24)           ;0x0018

LDR pc,(INT_Table + 28)           ;0x001C

 

    EXPORT  INT_Table

INT_Table

INT_Initialize_Addr     DCD INT_Initialize                                     

Undef_Instr_Addr        DCD Undef_Instr_ISR                                  

SWI_Addr                DCD SWI_ISR                                          

Prefetch_Abort_Addr     DCD Prefetch_Abort_ISR                          

Data_Abort_Addr         DCD Data_Abort_ISR

Undefined_Addr          DCD 0               ; NO LONGER USED

IRQ_Handler_Addr        DCD INT_IRQ_Parse  

FIQ_Handler_Addr        DCD INT_FIQ_Parse  

 

2)地址0x0处为RAM的情况,中断向量表还必须把中断向量表从ROM中复制到RAM的地址0x0开始的存储空间,如以下代码:

MOV     v5,#0

    ADR     v6,INT_Vectors

    LDMIA   v6!,{a1-v4}          ;复制中断向量表(8 words

    STMIA   v5!,{a1-v4}

    LDMIA   v6!,{a1-v4}          ;复制保存各中断处理函数地址。(8 words

    STMIA   v5!,{a1-v4}

三.多功能ARM开发板PB_ARM926EJ-S上的中断功能

ARM开发板支持多种功能,PCI接口、以太网口、音频设备、USB接口、LCD接口、外扩内存卡的功能,目前我们现在主要使用的功能是时钟(提供nucleus的系统时钟),UART串口(提供系统的输入与输出),PS/2键盘(提供系统的输入)。

ARM926EJ-S提供了两种中断信号:IRQFIQ,其实现的一般机制是,有一个向量中断控制器接收外部设备的中断信息,然后产生IRQ或者是FIQ中断信号,引导软件中断的执行。

开发板上的中断分为两组,主中断控制器和二级中断控制器,该两组没有主次之分,只是触发方式,即当中断发生后通知系统的顺序不一样而已。当有中断发生时,中断控制器状态位相应位置1,该位表示某个中断发生。

 

开发板上主要的寄存器如下表所示:

ADDRESS

Name

Access

Description

0x10140000

PICIRQStatus

READ

IRQ status register

0x10140010

PICIntEnable

READ/WRITE

Interrupt enable register

0x10140014

PICIntEnClear

WRITE

Interrupt enable clear register

 

PICIRQStatus中,常用的中断状态位如下所示(如相应为置1则表示对应中断发生,应该调用相应的中断处理函数):

15

14

13

12

11

10

9

8

7

6

5

4

3

2

1

0

SCI0

UART2

UART1

UART0

SSP

RTC

GPIO3

GPIO2

GPIO1

GPIO0

TIMER23

TIMER01

CommTX

CommRX

SoftWare

WDOG

在当前的系统中,我们用到了时钟中断,串口中断,还有在二级中断中的键盘中断(上表中暂未列出)。

 

 

四.Nucleus 系统简述

nucleus系统是实时嵌入式操作系统,具有占先、实时、多任务的内核,其中95%的代码是由C语言组成的,极易移植,广泛的应用在多种处理器当中;同时,nucleus开发包是开放源码的,方便开发工程师的配置,再加上nucleus的快速响应等其他特性,得到了广泛的应用。

4.1Nucleus系统的初始化过程

1.       最先执行的函数是INT_INITIALIZE,在该函数中负责完成与目标开发板对应的所需要的硬件的初始化,如一些ARM上的控制寄存器,中断向量表,系统堆栈等;该函数一般在INT.S或者是INT_PID.S中;

2.       INT_INITIALIZE执行完后,系统转入到nucleus上层的初始化中去,将控制权交给上层的函数INC_INITIALIZE,在该函数中完成nucleus各组件的初步的初始化工作,如线程、邮箱、管道、信号量、事件、定时器等,它的初始化只是给一些全局变量赋空值;

3.       INC_INITIALIZE中,在各组件初始化完之后,会调用一个用户自定义函数Application_Initialize(),在该函数中系统会调用用户真正的应用代码,如内存池的创建、建立任务、建立消息队列等等。总之,Application_Initialize()中的函数是用户真正需要建立的代码;

4.       在执行完Application_Initialize()之后,系统进入调度函数TCT_Schedule(),然后整个nucleus系统就运转起来。

 

如图所示:

 

INT_INITIALIZE

INC_INITIALIZE

各组件初始化

Application_Initialize()

TCT_Schedule()

系统执行

 

4.2 INT_INITIALIZE

针对我们的开发板,由于INT_INITIALIZE是系统运行的第一个函数,因此,在设置中,当系统reset后,此时执行地址为0x0000 0000中的代码,该地址中的代码跳转到函数INT_INITIALIZE()的地址,然后引导系统的运行。

INT_INITIALIZE() 函数主要执行的功能:

1)              将编译出来的已经初始化的数据从ROM中复制到RAM中,同时在RAM中建立ZI数据段,未初始化的数据;

2)              初始化异常中断向量表;

3)              设置ARM处理器对应的各种模式的nucleus的数据堆栈;

4)              设置对应的ARM开发板的硬件设置,在我们的需求中主要是一些中断的设置;

5    将控制权转移到INC_INITIALIZE

五.Nucleus在ARM开发板上的实现

Nucleus在我们的ARM开发板上能够运行起来,首先要使nucleus的硬件相关部分能够与开发板相互对应,由于现有的nucleus版本已经在ADS1.2环境下编译通过,所以就不用费力的更改汇编函数,只需要修改涉及到具体ARM板的寄存器参数的设置,其中主要的是操作系统运行所需要的时钟中断的设置。

由第二部分的表格可知,时钟中断在PICIRQStatus中的第四个bit置位,当ARM开发板上的时钟中断发生时,产生IRQ中断,相应位置位。

在具体的设置当中,使用了TIMER0产生的时钟中断,其基地址是0x101e2000,其偏移地址0x08TimerControl)是时钟控制寄存器,在其中设置相应的模式,如允许时钟运行模式,将时钟设

 

318

Bit 7

Bit 6

Bit 5

Bit 4

Bit 32

Bit 1

Bit 0

Reserved

TimerEn

TimerMode

IntEnable

reserved

TimerPre

TimerSize

OneShot

 

0-      Disable

1-      En

0-      freerun

1-      periodic

0-      disa

1-      IntEn

 

分频

0-      16bit

1-      32bit

0-      wrap

1-      oneshot

 

置成周期性时钟模式,允许时钟中断模式等。完成设置后,运行,当有时钟中断发生时,ARM产生IRQ中断,通过状态位判断后,nucleus调用相应的时钟中断函数进行处理,维持nucleus系统的时钟相关性功能的运行。

 

 

 

六.Nucleus中断机制的实现

nucleus系统初始化时,可参看文件int.s或者是int_pid.s,首先完成中断向量的配置,在IRQ中断处理过程中,由于IRQ中断有多个中断源,需要处理各种子程序,通过判断PICIRQStatus中断标志位后,确定具体的中断源,在调用相应的中断处理函数。以上是一般的中断处理流程。

Nucleus提供了两种中断机制,管理的中断和非管理的中断。

管理的中断需要向操作系统注册该中断向量,它与下文提到的非管理中断的主要区别是,该种中断的上下文保护由nucleus系统来完成保护;中断产生后通过该中断向量注册的低级中断服务程序(LISR)激活高级的中断服务程序(HISR)。LISR主要完成硬件的中断的处理,及激活HISRHISR的调度类似于任务,具有优先级,优先级比一般的任务要高,可以使用大多数的nucleus系统调用;

非管理的中断,该中断函数的地址直接的被放置到系统的中断向量表上,上下文的保存和恢复都需要用户自己来做,该种中断一般不能嵌套,否则可能会引起堆栈的出错,而且该种中断函数可以使用的nucleus系统调用比较少。

使用管理的中断的系统函数如下所示:

 

NU_Register_LISR(INT vector,                //中断向量

VOID (*new_lisr)(INT),      //低级中断处理函数

VOID (**old_lisr)(INT));     //保存的中断处理函数

 

NU_Create_HISR(NU_HISR *hisr, CHAR *name,  //创建高级中断处理函数

               VOID (*hisr_entry)(VOID), OPTION priority,

               VOID *stack_address, UNSIGNED stack_size)

 

NU_Activate_HISR(NU_HISR *hisr)            //由低级中断激活高级中断。

 

TCC_Dispatch_LISR(INT vector)                //该函数应用在int.s或者int_pid.s中,负责根据中断向量号,索引到不同的中断函数执行。

 

举个例子:

VOID   (*old_lisr)(INT);

void    Lisr_uart0(INT vector_number);

void    Hisr_uart0_proc(void);

NU_HISR  Hisr_uart0;

 

NU_Create_HISR(&Hisr_uart0,"HISR_TEST",Hisr_uart0_proc,2,pointer,1000); //高级中断函数

 

NU_Register_LISR(21,Lisr_uart0,&old_lisr)//登记低级中断函数

 

void    Lisr_uart0(INT vector_number)    //登记的低级中断函数

{

    unsigned int base;

//////////////////////////////////////////

    base = UART0_BASE;

    uart_data = HW_REG(base,0x00);

    l++;

 /////////////////////////////////////////

    NU_Activate_HISR(&Hisr_uart0);     //激活高级中断函数

}

 

void    Hisr_uart0_proc(void)    //高级中断函数

{

    ……

……  

}

 

除了以上常用的函数外,用户还需要自己定义低级中断函数和高级中断函数。

http://blog.csdn.net/lieye_leaves/article/details/7723318

0 0
原创粉丝点击