1. 程式人生 > >ARM的異常(S3C2440)

ARM的異常(S3C2440)

先釐清概念 異常:異常就是在程式執行過程中(USER mode),出現了一些異常情況,而中斷是異常的一種情況。 中斷:中斷是一種異常。

異常發生了會進入相應的模式。

ARM體系的CPU有7種工作模式:

  • User(使用者模式) : ARM處理器正常的程式執行狀態
  • FIQ(快速中斷模式) : 當一個高優先順序(fast) 中斷產生時將會進入這種模式
  • IRQ(中斷模式) : 通常的中斷產生時將會進入這種模式
  • Svc(管理模式) : 當復位或軟中斷指令執行時將會進入這種模式
  • Abt(中止模式) : 當資料或指令預取中止時將會進入這種模式
  • Und(未定義指令模式) : 當執行未定義指令時會進入這種模式
  • Sys(系統模式) : 使用和User模式相同暫存器集的特權模式,執行具有特權的作業系統任務

再看一下ARM在各模式下的暫存器 在這裡插入圖片描述 ARM一共有37個32位暫存器。其中31個通用暫存器和6個狀態暫存器。 圖中帶三角的暫存器在物理上是不同的暫存器。例如,R8_fiq和R8就屬於不同的物理暫存器。 再重點看一下程式狀態暫存器: 在這裡插入圖片描述

ARM對異常(中斷)處理過程 1 初始化: a 設定中斷源,讓它可以產生中斷 b 設定中斷控制器(可以遮蔽某個中斷,優先順序) c 設定CPU總開關,(使能中斷)

2 執行其他程式:正常程式

3 產生中斷:按下按鍵—>中斷控制器—>CPU

4 cpu每執行完一條指令都會檢查有無中斷/異常產生

5 發現有中斷/異常產生,開始處理。對於不同的異常,跳去不同的地址執行程式。這地址上,只是一條跳轉指令,跳去執行某個函式(地址),這個就是異常向量。如下就是異常向量表。 (3-5都是硬體強制做的) 在這裡插入圖片描述

6 這些函式做什麼事情? 軟體做的: a 儲存現場(各種暫存器) b 處理異常(中斷): 分辨中斷源 再呼叫不同的處理函式 c 恢復現場

發生異常時,我們的CPU會做什麼事情?

1 把下一條指令的地址儲存在LR暫存器裡(某種異常模式的LR等於被中斷的下一條指令的地址) 它有可能是PC + 4有可能是PC + 8,到底是那種取決於不同的情況

2 把CPSR儲存在SPSR裡面(某一種異常模式下SPSR裡面的值等於CPSR) 3

修改CPSR的模式為進入異常模式(修改CPSR的M4 ~ M0進入異常模式) 4 跳到向量表

退出異常怎麼做? 1 讓LR減去某個值,讓後賦值給PC(PC = 某個異常LR暫存器減去 offset) 減去什麼值呢? 也就是我們怎麼返回去繼續執行原來的程式,根據下面這個表來取值 在這裡插入圖片描述 如果發生的是SWI可以把 R14_svc複製給PC 如果發生的是IRQ可以把R14_irq的值減去4賦值給PC 2 把CPSR的值恢復(CPSR 值等於 某一個一場模式下的SPSR) 3 清中斷(如果是中斷的話,對於其他異常不用設定) S3C2440的資料手冊中也有相關描述: 在這裡插入圖片描述

程式設計 根據上面的分析,我們程式設計就是做3個任務: 1 儲存現場(儲存被中斷模式的暫存器) 就比如說我們的程式正在系統模式/使用者模式下執行,當你發生中斷時,需要把R0 ~ R14這些暫存器全部儲存下來,讓後處理異常,最後恢復這些暫存器 但如果是快中斷,那麼我就不需要儲存 系統/使用者模式下的R8 ~ R12這幾個暫存器,在FIQ模式下有自己專屬的R8 ~ R12暫存器,省略儲存暫存器的時間,加快處理速度 但是在Linux中並不會使用FIQ模式

2 處理異常

3 恢復現場

uboot是裸板程式的集大成者,看看uboot中對異常是怎麼程式設計的。 以下是uboot的start.S的部分程式。

 /*code: 28 -- 72*/
    #include <config.h>
    #include <version.h>
    
    
    /*
     *************************************************************************
     *
     * Jump vector table as in table 3.1 in [1]
     *
     *************************************************************************
     */
    #define GSTATUS2   (0x560000B4)
    #define GSTATUS3   (0x560000B8)
    #define GSTATUS4   (0x560000BC)
    
    #define REFRESH(0x48000024)
    #define MISCCR (0x56000080)
    
    #define LOCKTIME	0x4C000000	/* R/W, PLL lock time count register */
    #define MPLLCON		0x4C000004	/* R/W, MPLL configuration register */
    #define UPLLCON		0x4C000008	/* R/W, UPLL configuration register */
    #define CLKCON		0x4C00000C	/* R/W, Clock generator control reg. */
    #define CLKSLOW		0x4C000010	/* R/W, Slow clock control register */
    #define CLKDIVN		0x4C000014	/* R/W, Clock divider control */
    
    /******下面這些就是異常向量表*****/
    .globl _start
    _start:	b   reset
    	ldr	pc, _undefined_instruction
    	ldr	pc, _software_interrupt
    	ldr	pc, _prefetch_abort
    	ldr	pc, _data_abort
    	ldr	pc, _not_used
    	ldr	pc, _irq
    	ldr	pc, _fiq
    
    _undefined_instruction:	.word undefined_instruction
    _software_interrupt:	.word software_interrupt
    _prefetch_abort:	.word prefetch_abort
    _data_abort:		.word data_abort
    _not_used:		.word not_used
    _irq:			.word irq
    _fiq:			.word fiq

分析:從0地址開始,根據異常向量表中的地址,放了一條跳轉指令,發生異常之後,程式調到異常向量表的對應地址執行程式,然後跳轉到相應處理函式中。 通常這些都是放在start.S的開始部分,異常處理函式放在程式後面。 例:_undefined_instruction: .word undefined_instruction undefined_instruction是異常處理函式的標號,_undefined_instruction是異常處理函式對應的地址值,而ldr pc, _undefined_instruction是把這個地址值給PC。

注1:異常處理程式一般放在彙編的最後。 注2:重定位完成後,最好立即跳到SDRAM執行。

發生異常時,程式的具體執行流程: 在這裡插入圖片描述

下面是包含了未定義異常和定時器中斷異常的start.S。具體的函式處理過程由c語言實現。


.text
.global _start

_start: 
	b reset
	ldr	pc, und_addr		/* 遇到未定義指令,進入未定義模式 */
	ldr	pc, _software_interrupt
   	ldr	pc, _prefetch_abort
    ldr	pc, _data_abort
    ldr	pc, _not_used
    ldr	pc, irq_addr
    ldr	pc, _fiq

und_addr:
	.word undefined_instruction
	
_software_interrupt:
_prefetch_abort:
_data_abort:
_not_used:


irq_addr:
	.word irq_code
_fiq:
	
.align 4	
reset:	
	/* 關閉看門狗 */
	ldr r0, =0x53000000
	ldr r1, =0
	str r1, [r0]

	/* 設定MPLL, FCLK : HCLK : PCLK = 400m : 100m : 50m */
	/* LOCKTIME(0x4C000000) = 0xFFFFFFFF */
	ldr r0, =0x4C000000
	ldr r1, =0xFFFFFFFF
	str r1, [r0]

	/* CLKDIVN(0x4C000014) = 0X5, tFCLK:tHCLK:tPCLK = 1:4:8  */
	ldr r0, =0x4C000014
	ldr r1, =0x5
	str r1, [r0]

	/* 設定CPU工作於非同步模式 */
	mrc p15,0,r0,c1,c0,0
	orr r0,r0,#0xc0000000   //R1_nF:OR:R1_iA
	mcr p15,0,r0,c1,c0,0

	/* 設定MPLLCON(0x4C000004) = (92<<12)|(1<<4)|(1<<0) 
	 *  m = MDIV+8 = 92+8=100
	 *  p = PDIV+2 = 1+2 = 3
	 *  s = SDIV = 1
	 *  FCLK = 2*m*Fin/(p*2^s) = 2*100*12/(3*2^1)=400M
	 */
	ldr r0, =0x4C000004
	ldr r1, =(92<<12)|(1<<4)|(1<<0)
	str r1, [r0]

	/* 一旦設定PLL, 就會鎖定lock time直到PLL輸出穩定
	 * 然後CPU工作於新的頻率FCLK
	 */
	
	

	/* 設定記憶體: sp 棧 */
	/* 分辨是nor/nand啟動
	 * 寫0到0地址, 再讀出來
	 * 如果得到0, 表示0地址上的內容被修改了, 它對應ram, 這就是nand啟動
	 * 否則就是nor啟動
	 */
	mov r1, #0
	ldr r0, [r1] /* 讀出原來的值備份 */
	str r1, [r1] /* 0->[0] */ 
	ldr r2, [r1] /* r2=[0] */
	cmp r1, r2   /* r1==r2? 如果相等表示是NAND啟動 */
	ldr sp, =0x40000000+4096 /* 先假設是nor啟動 */
	moveq sp, #4096  /* nand啟動 */
	streq r0, [r1]   /* 恢復原來的值 */

	bl sdram_init
	

	/* 重定位text, rodata, data段整個程式 */
	bl copy2sdram

	/* 清除BSS段 */
	bl clean_bss
	
	/* 復位之後, cpu處於svc模式
	 * 現在, 切換到usr模式
	 */
	mrs r0, cpsr         /* 讀出cpsr */
	bic r0, r0, #0xf     /* 修改M4-M0為0b10000, 進入usr模式 */
	bic r0, r0, #(1<<7)  /* 清除I位, 使能中斷 */
	msr cpsr, r0
	
	/* 設定usr模式下的棧 */
	ldr sp, =0x33f00000
	
	ldr pc, =sdram		/* 重定位已完成,跳轉到SDRAM執行 */
sdram:

	
	//bl main  /* 使用BL命令相對跳轉, 程式仍然在NOR/sram執行 */
	ldr pc, =main  /* 絕對跳轉, 跳到SDRAM */

halt:
	b halt


.align 4	
undefined_instruction:
	ldr sp, =0x34000000
	
	/* 保護現場 */
	stmdb sp!, {r0-r12,lr}
	/* 處理異常 */
	
	/* 恢復現場 */
	ldmia sp!, {r0-r12,pc}^	

.align 4
irq_code:
	ldr sp, =0x33d00000
	
	/* 保護現場 */
	sub lr, lr, #4
	stmdb sp!, {r0-r12,lr}
	/* 處理異常 */
	bl timer_isr
	/* 恢復現場 */
	ldmia sp!, {r0-r12,pc}^