【S3C2440】第14課、異常與中斷之學習筆記

第14課、異常與中斷
節4、und異常模式程序示例
1、30000020 <und_string>:
(省略)
3000003c: 216e6f69 cmncs lr, r9, ror #30

30000041 :
(省略)
答:因此,應在string之後,進行.align 4 操作;
ALIGN

2、關於未定義異常指令的跳轉的正確格式
0) b do_und //可正常跳轉,但是當Nand啓動且該彙編文件過大時,會造成未定義異常處理程序中的函數在SRAM的4K之外

  1. ldr pc, do_und
  2. ldr pc, =do_und
  3. ldr pc, und_addr
    und_addr:
    .word do_und
    查看兩組的反彙編,探究正確用法
1)方法1:
	_start:
		b reset
		ldr pc, do_und
	do_und:		
	und_code:
		bl print4
		.word 0xdeadc0de
	und_code:
		bl print4
		.word 0xdeadc0de
		bl print2

不可以正常打印,反覆打印:
2_123
1_abc
4_678
反彙編如下:
30000000 <_start>:
30000000: ea000012 b 30000050
30000004: e51ff004 ldr pc, [pc, #-4] ; 30000008 <do_und>
30000008 <do_und>:
300000c8 <und_code>:
300000c8: eb00011f bl 3000054c
300000cc: deadc0de mcrle 0, 5, ip, cr13, cr14, {6}
300000d0: eb00010f bl 30000514 <pri2)方法2:
_start:
b reset
ldr pc, =do_und
do_und:
und_code:
bl print4
.word 0xdeadc0de
und_code:
bl print4
.word 0xdeadc0de
bl print2
可以正常打印,打印如下:
2_123
1_abc
4_678
Exception! cpsr = 0x600000db
Undefined instruction exception!
2_123

Wakakaka
0x00000000
0xabcdef12
0x00000012
0x0000000c
Aa1Bb2Cc3Dd4Ee5Ff6Gg7Hh8Ii9Jj:Kk;Ll<Mm=

反彙編如下:
30000000 <_start>:
30000000: ea000012 b 30000050
30000004: e59ff0d0 ldr pc, [pc, #208] ; 300000dc <.text+0xdc>
30000008 <do_und>:
300000d8 :
300000d8: eafffffe b 300000d8
300000dc: 30000008 andcc r0, r0, r8
300000c8 <und_code>:
300000c8: eb00011f bl 3000054c
300000cc: deadc0de mcrle 0, 5, ip, cr13, cr14, {6}
300000d0: eb00010f bl 30000514 <pri3)方法3:
_start:
b reset
ldr pc, und_addr
und_addr:
.word do_und
do_und:
und_code:
bl print4
.word 0xdeadc0de
und_code:
bl print4
.word 0xdeadc0de
bl print2
可以正常打印,打印結果如下:
2_123
1_abc
4_678
Exception! cpsr = 0x600000db
Undefined instruction exception!
2_123

Wakakaka
0x00000000
0xabcdef12
0x00000012
0x0000000c
Aa1Bb2Cc3Dd4Ee5Ff6Gg7Hh8Ii

反彙編如下:
30000000 <_start>:
30000000: ea000012 b 30000050
30000004: e51ff004 ldr pc, [pc, #-4] ; 30000008 <und_addr>
30000008 <und_addr>:
30000008: 3000000c andcc r0, r0, ip
3000000c <do_und>:
300000c8 <und_code>:
300000c8: eb00011f bl 3000054c
300000cc: deadc0de mcrle 0, 5, ip, cr13, cr14, {6}
300000d0: eb00010f bl 30000514

3、
現象1:反覆打印
123
abc
123
abc
現象2:打印一次
2_123
1_abc
4_678
3_345
Exception! cpsr = 0x600000db
Undefined instruction exception!

現象分析和解決方案:
現象1分析:程序反覆執行reset, sdram, 未定義指令(並未進入未定義處理程序);
bug指令:ldr pc, do_und
現象2分析:程序執行reset, sdram, 未定義指令並跳轉到未定義指令(進入未定義處理程序);
bug指令:ldmia sp!, {r0-r12, pc}後的‘^’未加;

4、關於彙編字符串的引用的方法

ldr r1, =und_string
	bl print_exception
und_string:
	.string "Undefined instruction exception!"

ldr r1, =.string或者 ldr r1, =string
bl print_exception
.string "Undefined instruction exception!"果:Error!
start.o(.text+0xd0): In function halt': : undefined reference to.string’

5、關於異常與中斷跳轉指令 b do_und;的隱患:b do_und爲相對跳轉,異常處理程序do_und內的函數
調用指令 bl print_exception也是相對跳轉,如果Nand啓動,函數print_exception在4K之外,該指令
必定出錯!因此,爲保險,應該在重定位之後就將程序進入SDRAM執行!

修改代碼:

_start:
		ldr pc, =do_und
		ldr pc, =do_swi
	reset:
		ldr pc, =sdram

但是,問題:ldr Rd, =xxx; 對目標寄存器Rd的賦值操作,是先把xxx(地址)的值保存到內存中,
然後再讀出該內存空間中的值(所代表的地址)賦值給目標寄存器Rd。xxx所保存的內存由編譯器
來決定,通常放在該彙編文件的結尾(如)的內存空間中。當start.S很大且Nand啓動時,
有可能去SRAM的4K空間之外取值(地址)do_und/do_swi/…。爲防止彙編大小大於4K,Nand啓動時,
4K之外的內存無法被讀取到,現修改do_und/do_swi/…的內存空間到SRAM的前面。
修改代碼:

_start:
		b reset          /* vector 0 : reset */
		ldr pc, und_addr /* vector 4 : und */
		ldr pc, swi_addr /* vector 8 : swi */
	und_addr:
		.word do_und
	swi_addr:
		.word do_swi
	do_und:	...
	do_swi:	...

但是,問題:ldr Rd, =sdram/sp/0x4C000014/…
異常與中斷的跳轉和處理程序經過設置已經在SRAM/Norflash的空間的前面存儲和運行,但是在reset:內,
重定位之前的一些存儲僞指令:ldr Rd, =sdram/0x4C000014/sp, 的存儲空間也應彙編文件的結尾(如)
的內存空間中,這種情況怎麼處理?
其中,實例代碼如下:
(0.)代碼do_und: ldr sp, =0x34000000的反彙編如下:
30000014 <do_und>:
30000014: e3a0d30d mov sp, #872415232 ; 0x34000000
所以,僞指令ldr sp, =0x34000000類型不論;
(1.)ldr pc, =do_und/do_swi/…/und_string/swi_string由於異常處理跳轉到SDRAM執行,不論;
(2.)ldr pc, =main是在ldr pc, =sdram之後跳轉到SDRAM中執行的,也不論;
(3.)ldr pc, =sdram僞指令的反彙編代碼如下:
30000090 :
30000108: e59ff038 ldr pc, [pc, #56] ; 30000148 <.text+0x148>
3000010c :

30000124 :

30000148: 3000010c andcc r0, r0, ip, lsl #2
(4.)reset:
/* 關閉看門狗 /
ldr r0, =0x53000000
ldr r1, =0
ldr r0, =0x4C000014(CLKDIVN)的反彙編:
30000090 :
30000090: e3a00453 mov r0, #1392508928 ; 0x53000000
30000094: e3a01000 mov r1, #0 ; 0x0
3000009c: e3a00313 mov r0, #1275068416 ; 0x4c000000
300000a8: e59f0084 ldr r0, [pc, #132] ; 30000134 <.text+0x134>
30000124 :
30000134: 4c000014 stcmi 0, cr0, [r0], {20}
--------------------------------------------------------------------
reset:
ldr sp, =0x40000000+4096 /
先假設是nor啓動 /的反彙編如下:
30000090 :
300000e0: e59fd058 ldr sp, [pc, #88] ; 30000140 <.text+0x140>
30000124 :
30000140: 40001000 andmi r1, r0, r0
-------------------------------------------------------------------
reset:(結尾處)
/
設置 sp_usr */
ldr sp, =0x33f00000
ldr pc, =sdram的反彙編如下:
30000104: e59fd038 ldr sp, [pc, #56] ; 30000144 <.text+0x144>
30000108: e59ff038 ldr pc, [pc, #56] ; 30000148 <.text+0x148>
30000124 :
30000144: 33f00000 mvnccs r0, #0 ; 0x0
30000148: 3000010c andcc r0, r0, ip, lsl #2
對情景(3.)(4.)的分析,正如前面把【ldr pc, =do_und;】改爲【ldr pc, und_addr; und_addr:.word do_und; 】的擔憂一樣,
ldr Rd, =xxx; 對目標寄存器Rd的賦值操作,是先把xxx(地址)的值保存到內存中,然後再讀出該內存空間中的值(所代表的地址)
賦值給目標寄存器Rd。xxx所保存的內存由編譯器來決定,通常放在該彙編文件的結尾(如)的內存空間中。
當start.S很大且Nand啓動時,有可能去SRAM的4K空間之外取值(地址)sdram/0x4C000014/sp,需不需要修改標號sdram的內存到SRAM
的前面?還有,對於特殊功能寄存器和sp棧的設置的讀存操作如何處理?
對於這些僞指令的使用,可不可以使用mov/movs來代替其功能?

任務:
1、驗證上面的猜想
2、向程序1
3、次而程序2

節7、按鍵外部中斷程序的疑惑:
1、調用其他文件的函數,不需要提前聲明嗎?
例如:

include "s3c2440_soc.h"
 #include "uart.h"
  #include "init.h"

int main(void)
{
	led_init();
	interrupt_init();  /* 初始化中斷控制器 */
	key_eint_init();   /* 初始化按鍵, 設爲中斷源 */
	...
}

中main()函數開頭的三個函數都沒有聲明直接調用,而且編譯完全沒有問題;
答:(查帖:https://bbs.csdn.net/topics/390994678?page=1 :沒有聲明的函數爲什麼可以調用?)
a.在c語言中全局的函數和變量是對每個文件可見的,但在別的文件中變量需要用extern聲明,纔可用;而函數不需要
b.函數不需extern聲明,即可在其他的文件可用,而變量必須用extern聲明纔可用。
c.只要聲明全局變量就默認 前面加extern(程序員可以不加,但編譯器默認加上)
d.早期的編譯器允許這種做法,再現在的C標準要求函數使用前必須先聲明,但看在那些業已存在的
大量程序的面子上,有很多編譯器直到現在還容許這種做法。

2、2440手冊中,GPFDAT [7:0] 位 描述 初始狀態
當端口配置爲輸入端口時,相應位爲引腳狀態。
當端口配置爲輸出端口時,引腳狀態將與相應位相同。
當端口配置爲功能引腳,將讀取到未定義值。
問題:GPF作爲外部中斷引腳時,是不是說GPFDAT爲隨機值不可用?
但爲什麼視頻中的代碼把GPFDAT作爲 if() 的條件判斷成功編譯且燒寫成功?
能燒寫成功是不是說 “當端口配置爲功能引腳,和配置爲輸出端口時一樣,引腳狀態將與相應位相同”?
第14課,第7節的視頻和代碼

3、按鍵外部中斷其中兩個按鍵不管用的原因查找
只有一個鍵的中斷有用,但也證明
初始化外部中斷源時,其中兩個鍵的GPGCON寫成GPFCON!
程序調試方法:驗證並調換出出錯的文件,然後與正確文件逐行對比查找原因

第8節:
普通變量的定義格式: int i;
函數指針變量的定義格式: int (*p)(int, int)
區別: 一個變量名在表達式後面,一個變量名在表達式中間!

函數指針的本質還是指針,是變量!
方式1:
int max(int, int);
int min(int, int);
int (*p)(int, int);
方式2:
int fun(int, int, int(*p)(int, int))
int max(int, int);
int min(int, int);
總結:方式1還是建立一個和原函數平等的函數,然後通過這個通用的函數調用同類的函數;
方式2已經把原函數的參數和其函數指針作爲形參全部歸到一個上一級的新的函數內,這樣調用新的函數,更加直觀直接

typedef的用法:
1)
2)命名一個新的類型名代表數組類型
typedef int Arr[10];

4)命名一個新的類型名代表指向函數的指針類型:
typedef int(*Pointer)();
Pointer pf;
作用:採用如同定義變量的方法那樣先生成一個類型名,然後用它去定義變量。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章