S5PV210裸机之外部中断
S5PV210中斷體系介紹
一、什么是中斷??
(1)中斷的發明是用來解決宏觀上的并行需要的。宏觀就是從整體上來看,并行就是多件事情都完成了。?
(2)微觀上的并行,就是指的真正的并行,就是精確到每一秒甚至每一刻,多個事情都是在同時進行的。宏觀上面的并行并不等于圍觀的并行,有時候宏觀上是并行的,微觀上是串行的。?
(3)例子中一個人在看電影,快遞來了暫停電影跑去收快遞,收完快遞繼續回來看電影,這個例子就是宏觀上的并行和微觀上的串行。例子中一個人等同于SoC中1個CPU(也就是單核CPU),這個CPU看電影就不能收快遞,收快遞就不能看電影(也就是說不能真正的并行)。單核心CPU在微觀角度是串行的,但是因為CPU很快,所以在宏觀看來可以并行。?
(4)上例中大部分時間在看電影,中間少量時間去收快遞,那么類比于CPU來說,看電影就應該是CPU的常規任務,而收快遞則應該是中斷例程。也就是說CPU平時一直在進行看電影任務,等快遞來了(中斷發生了)快遞員(類似于中斷源)會打電話叫人去收快遞(中斷源會觸發中斷通知CPU去處理中斷),人收到電話(CPU收到中斷信號)后會暫定電影(CPU保存常規任務的現場)跑去收快遞(CPU去執行中斷處理程序ISR處理中斷),收完快遞(執行完ISR)回來繼續看電影(CPU恢復常規任務的現場,繼續執行常規任務)?
(5)為什么需要中斷?因為單核CPU實際無法并行的,但是通過中斷機制,可以實現假并行(宏觀上的并行,微觀上實際還是串行的)。?
二、 Soc對中斷的實現機制:異常向量表?
(1)異常向量表是CPU中某些特定地址的特定定義。當中斷發生的時候,中斷要想辦法通知CPU去處理中斷,怎么做到?這就要靠異常向量表。?
(2)在CPU設計時,就事先定義了CPU中一些特定地址作為特定異常的入口地址(譬如定義0x00000000地址為復位異常向量地址,則發生復位異常時CPU會自動跳轉到0x00000000地址去執行指令。又譬如外部中斷對應的異常向量地址為0x30000008,則發生外部中斷后,CPU會硬件自動跳轉到0x30000008地址去執行指令。)如?
(3)以上講的是CPU硬件設計時對異常向量表的支持,下來就需要軟件支持了。硬件已經決定了發生什么異常CPU自動跳轉PC到哪個地址去執行,軟件需要做的就是把處理這個異常的代碼的首地址填入這個異常向量地址。?
三、S5PV210的異常向量表?
(1)s5pv210異常向量表的基地址為0xD003_7400,下圖為異常對于基地址的偏移量;?
?
?
(2)異常向量表中各個向量的相對位置是固定的,但是他們的起始地址是不固定的,各種SoC可以不一樣,而且復雜ARM中還可以讓用戶來軟件設置這個異常向量表的基地址;?
(3)擴展到所有架構的CPU中:所有架構(譬如51單片機、PIC單片機)的CPU實現中斷都是通過異常向量表實現的,這個機制是不變的;但是不同CPU異常向量表的構造和位置是不同的。?
四、異常和中斷的區別和聯系?
(1)針對SoC來說,發生復位、軟中斷、中斷、快速中斷、取指令異常、數據異常等,我們都統一叫異常。所以說:中斷其實是異常的一種。
(2)異常的定義就是突發事件,打斷了CPU的正常常規業務,CPU不得不跳轉到異常向量表中去執行異常處理程序;中斷是異常的一種,一般特指SoC內的內部外設產生的打斷SoC常規業務,或者外部中斷(SoC的GPIO引腳傳回來的中斷)。
五、 S5PV210異常向量表的編程處理?
1、像內存一樣去訪問異常向量表?
(1)S5PV210的異常向量表可以改變(在CP15協處理器中),以適應操作系統的需求。但是目前系統剛啟動時,此時DRAM尚未初始化,程序都在SRAM中運行。210在iRAM中設置了異常向量表,供暫時性使用。?
(2)查210的iROM application note文檔中iRAM的地址分配,可知,iRAM中的異常向量表起始地址為0xD0037400。知道了異常向量表的起始地址后,各個異常對應的入口就很好知道了。?
2、函數名的實質就是函數的首地址?
(1)函數名在C語言中的理解方法和變量名其實沒區別。編譯器會把這個函數的函數體對應的代碼段和這個函數的函數名(實質是符號)對應起來,等我們在使用這個函數名符號時,編譯器會將函數的函數體實際上做替換。因為函數體都不止4字節,而函數名這個符號只能對應1個地址,所以實際對應的是函數體那一個代碼段的首地址。?
(2)拿C語言中的語法來講,函數名就是這個函數的函數指針。?
總結:當我們將異常處理程序的首地址和異常向量表綁定起來后,異常處理初步階段就完成了。到目前可以保證相應異常發生后,硬件自動跳轉到對應異常向量表入口去執行時,可以執行到我們事先綁定的函數。?
總結:當我們將異常處理程序的首地址和異常向量表綁定起來后,異常處理初步階段就完成了。到目前可以保證相應異常發生后,硬件自動跳轉到對應異常向量表入口去執行時,可以執行到我們事先綁定的函數。?
3、為什么中斷處理要先在匯編中進行?
(1)中斷處理要注意保護現場(中斷從SVC模式來,則保存SVC模式下的必要寄存器的值)和恢復現場(中斷處理完成后,準備返回SVC模式前,要將保存的SVC模式下的必要寄存器的值恢復回去,不然到了SVC模式后寄存器的值亂了,SVC模式下原來正在進行的常規任務就被你搞壞了)?
(2)保存現場包括:第一:設置IRQ棧;第二,保存LR;第三,保存R0~R12?
(3)為什么要保存LR寄存器?要考慮中斷返回的問題。中斷ISR執行完后如何返回SVC模式下去接著執行原來的代碼。中斷返回其實取決于我們進入中斷時如何保存現場。中斷返回時關鍵的2個寄存器就是PC和CPSR。所以我們在進入IRQ模式時,應該將SVC模式下的下一句指令的地址(中斷返回地址)和CPSR保存起來,將來恢復時才可以將中斷返回地址給PC,將保存的CPSR給CPSR。?
(4)中斷返回地址就保存在LR中,而CPSR(自動)保存在(IRQ模式下的)SPSR中?
4、匯編保存現場和恢復現場
(1)保護現場關鍵是保存:中斷處理程序的返回地址,r0-r12(cpsr是自動保存的)
(2)恢復現場主要是恢復:r0-r12,pc,cpsr
六、 S5PV210的向量中斷控制寄存器?
1、異常處理的2個階段?
可以將異常處理分為2個階段來理解。第一個階段是異常向量表跳轉;第二個階段就是進入了真正的異常處理程序irq_handler之后的部分。
(1)第一個階段之所以能夠進行,主要依賴于CPU設計時提供的異常向量表機制。第一個階段的主要任務是從異常發生到響應異常并且保存/恢復現場、跳轉到真正的異常處理程序處。?
(2)第二個階段的目的是識別多個中斷源中究竟哪一個發生了中斷,然后調用相應的中斷處理程序來處理這個中斷。
2、 S3C2440的第二階段處理過程
(1)第一個問題,怎么找到具體是哪個中斷:S3C2440的中斷控制器中有一個寄存器(32位的),寄存器的每一個位對應一個中斷源(為了解決支持更多中斷源,2440又設計了
一個子中斷機制。在一級中斷寄存器中有一些中斷是共用的一個bit位,譬如AC97和WDT。對于共用中斷,用子中斷來區分究竟是哪一個發生了中斷)
(2)第二個問題,怎么找到對應的isr的問題:首先給每個中斷做了個編號,進入isr_handler之后先通過查閱中斷源寄存器和子中斷寄存器(中哪一位為1)確定中斷的編號,然后
用這個編號去isr數組(isr數組是中斷初始化時事先設定好的,就是把各個中斷的isr的函數名組成一個數組,用中斷對應的編號作為索引來查詢這個數組)中查閱得到isr地址。
評價:2440的中斷處理設計不是特別優秀:第一個過程中使用子中斷搞成2級的很麻煩;第二個過程中計算中斷編號是個麻煩事,很耗費時間。而中斷處理的時間是很寶貴的(
系統有一個性能指標,叫實時性。實時性就是中斷發生到響應的時間,這個時間越短越好。)
3、S5PV210的第二階段處理過程
(1)第一個問題,怎么找到具體是哪個中斷:S5PV210中因為支持的中斷源很多,所以直接設計了4個中斷寄存器,每個32位,每位對應一個中斷源。(理論上210最多支持128個中斷,實際支持不足128個,有些位是空的);210沒有子中斷寄存器,每個中斷源都是并列的。當中斷發生時,在irq_handler中依次去查詢4個中斷源寄存器,看哪一個的哪一位被置1,則這個位對應的寄存器就發生了中斷,即找到了中斷編號。
(2)第二個問題,怎么找到對應的isr的問題:210中支持的中斷源多了很多,如果還使用2440的那一套來尋找isr地址就太慢了,太影響實時性了。于是210開拓了一種全新的尋找isr的機制。210提供了很多寄存器來解決每個中斷源對應isr的尋找問題,具體尋找過程和建立過程見下節,實現的效果是當發生相應中斷時,硬件會自動的將相應isr推入一定的寄存器中,我們軟件只要去這個寄存器中執行函數就行了。
4、總結:第一階段都相同,第二階段各不同
(1)第一階段(異常向量表階段)2440和210幾乎是完全相同的。實際上幾乎所有的CPU在第一階段都是相同的。
(2)第二階段就彼此不同了。各個SoC根據自己對實時性的要求,和支持的中斷源的多少,各自發明了各自處理中斷,找到中斷編號,進一步找到對應isr地址的方式。
七、 S5PV210中斷處理的主要寄存器?
1、VICnINTENABLE和VICnINTENCLEAR?
(1)VICnINTENABLE 對應interrupt enable,INTENCLEAR對應interrupt enable clear?
(2)INTENABLE寄存器負責相應的中斷的使能,INTENCLEAR寄存器負責相應的中斷的禁止。?
(3)當我們想使能(意思就是啟用這個中斷,意思就是當硬件產生中斷時CPU能接收的到)某個中斷時,只要在這個中斷編號對應的VICnINTENABLE的相應bit位寫1即可(注意這個位寫1其他位寫0對其他位沒有影響);如果我們想禁止某個中斷源時,只要向VICnINTENCLEAR中相應的位寫1即可。注意:這里的設計一共有2種:有些CPU是中斷使能和禁止是一個寄存器位,寫1就使能寫0就禁止(或者反過來寫1就禁止寫0就使能),這樣的中斷使能設計就要非常小心,要使用我們之前說過的讀改寫三部曲來操作;?
另一種就是使能和禁止分開為2個寄存器,要使能就寫使能寄存器,要禁止就寫禁止寄存器。這樣的好處是我們使能/禁止操作時不需要讀改寫,直接寫即可。
2、VICnINTSELECT(Interrupt Select Register,中斷模式選擇寄存器,0選擇IRQ ,1FIQ)?
(1)設置各個中斷的模式為irq還是fiq。一般都設置成irq?
(2)IRQ和FIQ究竟有何區別。210中支持2種中斷,irq和fiq。irq是普通中斷,fiq是快速中斷。快速中斷提供一種更快響應處理的中斷通道,用于對實時性要求很高的中斷源。fiq在CPU設計時預先提供了一些機制保證fiq可以被快速處理,從而保證實時性。fiq的限制就是只能有一個中斷源被設置為fiq,其他都是irq。?
(3)CPU如何保證fiq比irq快?有2個原因:第一,fiq模式有專用的r8~r12,因此在fiq的isr中可以直接使用r8-r12而不用保存,這就能節省時間;第二,異常向量表中fiq是最后一個異常向量入口。因此fiq的isr不需要跳轉,可以直接寫在原地,這樣就比其他異常少跳轉一次,省了些時間。
3、VICnIRQSTATUS和VICnFIQSTATUS?
(1)中斷狀態寄存器,是只讀的。當發生了中斷時,硬件會自動將該寄存器的對應位置為1,表示中斷發生了。軟件在處理中斷第二階段的第一階段,就是靠查詢這個寄存器來得到中斷編號的。?
4、VICnVECTPRIORITY0~VICnVECTPRIORITY31?
(1)中斷優先級設置寄存器,設置多個中斷同時發生時先處理誰后處理誰的問題。一般來說高優先級的中斷可以打斷低優先級的中斷,從而嵌套處理中斷。當然了有些硬件/軟件可以設置不支持中斷嵌套。?
5、VICnVECTADDR0~VICnVECTADDR31、VICnADDR
(1)這三個寄存器和210中斷處理第二階段的第二階段有關。?
(2)VICnVECTADDR0到31這32個寄存器分別用來存放真正的各個中斷對應的isr的函數地址。相當于每一個中斷源都有一個VECTADDR寄存器,程序員在設置中斷的時候,把這個?
中斷的isr地址直接放入這個中斷對應的VECTADDR寄存器即可。?
(3)VICnADDR這個寄存器是只需要讀的,它里面的內容是由硬件自動設置的。當發生了相應中斷時,硬件會自動識別中斷編號,并且會自動找到這個中斷的VECTADDR寄存器,然?
后將其讀出復制到VICnADDR中,供我們使用。這樣的設計避免了軟件查找中斷源和isr,節省了時間,提高了210的中斷響應速度。?
八、S5PV210中斷處理的編程實踐?
start.s文件
interrupt.c文件
#include "int.h" #include "stdio.h"void reset_exception(void) {printf("reset_exception.\n"); }void undef_exception(void) {printf("undef_exception.\n"); }void sotf_int_exception(void) {printf("sotf_int_exception.\n"); }void prefetch_exception(void) {printf("prefetch_exception.\n"); }void data_exception(void) {printf("data_exception.\n"); }// 主要功能:綁定第一階段異常向量表;禁止所有中斷;選擇所有中斷類型為IRQ; // 清除VICnADDR為0 void system_init_exception(void) {// 第一階段處理,綁定異常向量表r_exception_reset = (unsigned int)reset_exception;r_exception_undef = (unsigned int)undef_exception;r_exception_sotf_int = (unsigned int)sotf_int_exception;r_exception_prefetch = (unsigned int)prefetch_exception;r_exception_data = (unsigned int)data_exception;r_exception_irq = (unsigned int)IRQ_handle;r_exception_fiq = (unsigned int)IRQ_handle;// 初始化中斷控制器的基本寄存器intc_init(); }// 清除需要處理的中斷的中斷處理函數的地址 void intc_clearvectaddr(void) {// VICxADDR:當前正在處理的中斷的中斷處理函數的地址VIC0ADDR = 0;VIC1ADDR = 0;VIC2ADDR = 0;VIC3ADDR = 0; }// 初始化中斷控制器 void intc_init(void) {// 禁止所有中斷// 為什么在中斷初始化之初要禁止所有中斷?// 因為中斷一旦打開,因為外部或者硬件自己的原因產生中斷后一定就會尋找isr// 而我們可能認為自己用不到這個中斷就沒有提供isr,這時它自動拿到的就是亂碼// 則程序很可能跑飛,所以不用的中斷一定要關掉。// 一般的做法是先全部關掉,然后再逐一打開自己感興趣的中斷。一旦打開就必須// 給這個中斷提供相應的isr并綁定好。VIC0INTENCLEAR = 0xffffffff;VIC1INTENCLEAR = 0xffffffff;VIC2INTENCLEAR = 0xffffffff;VIC3INTENCLEAR = 0xffffffff;// 選擇中斷類型為IRQVIC0INTSELECT = 0x0;VIC1INTSELECT = 0x0;VIC2INTSELECT = 0x0;VIC3INTSELECT = 0x0;// 清VICxADDRintc_clearvectaddr(); }// 綁定我們寫的isr到VICnVECTADDR寄存器 // 綁定過之后我們就把isr地址交給硬件了,剩下的我們不用管了,硬件自己會處理 // 等發生相應中斷的時候,我們直接到相應的VICnADDR中去取isr地址即可。 // 參數:intnum是int.h定義的物理中斷號,handler是函數指針,就是我們寫的isr// VIC0VECTADDR定義為VIC0VECTADDR0寄存器的地址,就相當于是VIC0VECTADDR0~31這個 // 數組(這個數組就是一個函數指針數組)的首地址,然后具體計算每一個中斷的時候 // 只需要首地址+偏移量即可。 void intc_setvectaddr(unsigned long intnum, void (*handler)(void)) {//VIC0if(intnum<32){*( (volatile unsigned long *)(VIC0VECTADDR + 4*(intnum-0)) ) = (unsigned)handler;}//VIC1else if(intnum<64){*( (volatile unsigned long *)(VIC1VECTADDR + 4*(intnum-32)) ) = (unsigned)handler;}//VIC2else if(intnum<96){*( (volatile unsigned long *)(VIC2VECTADDR + 4*(intnum-64)) ) = (unsigned)handler;}//VIC3else{*( (volatile unsigned long *)(VIC3VECTADDR + 4*(intnum-96)) ) = (unsigned)handler;}return; }// 使能中斷 // 通過傳參的intnum來使能某個具體的中斷源,中斷號在int.h中定義,是物理中斷號 void intc_enable(unsigned long intnum) {unsigned long temp;// 確定intnum在哪個寄存器的哪一位// <32就是0~31,必然在VIC0if(intnum<32){temp = VIC0INTENABLE;temp |= (1<<intnum); // 如果是第一種設計則必須位操作,第二種設計可以// 直接寫。VIC0INTENABLE = temp;}else if(intnum<64){temp = VIC1INTENABLE;temp |= (1<<(intnum-32));VIC1INTENABLE = temp;}else if(intnum<96){temp = VIC2INTENABLE;temp |= (1<<(intnum-64));VIC2INTENABLE = temp;}else if(intnum<NUM_ALL){temp = VIC3INTENABLE;temp |= (1<<(intnum-96));VIC3INTENABLE = temp;}// NUM_ALL : enable all interruptelse{VIC0INTENABLE = 0xFFFFFFFF;VIC1INTENABLE = 0xFFFFFFFF;VIC2INTENABLE = 0xFFFFFFFF;VIC3INTENABLE = 0xFFFFFFFF;}}// 禁止中斷 // 通過傳參的intnum來禁止某個具體的中斷源,中斷號在int.h中定義,是物理中斷號 void intc_disable(unsigned long intnum) {unsigned long temp;if(intnum<32){temp = VIC0INTENCLEAR;temp |= (1<<intnum);VIC0INTENCLEAR = temp;}else if(intnum<64){temp = VIC1INTENCLEAR;temp |= (1<<(intnum-32));VIC1INTENCLEAR = temp;}else if(intnum<96){temp = VIC2INTENCLEAR;temp |= (1<<(intnum-64));VIC2INTENCLEAR = temp;}else if(intnum<NUM_ALL){temp = VIC3INTENCLEAR;temp |= (1<<(intnum-96));VIC3INTENCLEAR = temp;}// NUM_ALL : disable all interruptelse{VIC0INTENCLEAR = 0xFFFFFFFF;VIC1INTENCLEAR = 0xFFFFFFFF;VIC2INTENCLEAR = 0xFFFFFFFF;VIC3INTENCLEAR = 0xFFFFFFFF;}return; }// 通過讀取VICnIRQSTATUS寄存器,判斷其中哪個有一位為1,來得知哪個VIC發生中斷了 unsigned long intc_getvicirqstatus(unsigned long ucontroller) {if(ucontroller == 0)return VIC0IRQSTATUS;else if(ucontroller == 1)return VIC1IRQSTATUS;else if(ucontroller == 2)return VIC2IRQSTATUS;else if(ucontroller == 3)return VIC3IRQSTATUS;else{}return 0; }// 真正的中斷處理程序。意思就是說這里只考慮中斷處理,不考慮保護/恢復現場 void irq_handler(void) {printf("irq_handler.\n");// SoC支持很多個(在低端CPU例如2440中有30多個,在210中有100多個)中斷// 這么多中斷irq在第一個階段走的是一條路,都會進入到irq_handler來// 我們在irq_handler中要去區分究竟是哪個中斷發生了,然后再去調用該中斷// 對應的isr。// 雖然硬件已經自動幫我們把isr放入了VICnADDR中,但是因為有4個,所以我們必須// 先去軟件的檢查出來到底哪個VIC中斷了,也就是說isr到底在哪個VICADDR寄存器中unsigned long vicaddr[4] = {VIC0ADDR,VIC1ADDR,VIC2ADDR,VIC3ADDR};int i=0;void (*isr)(void) = NULL;for(i=0; i<4; i++){// 發生一個中斷時,4個VIC中有3個是全0,1個的其中一位不是0if(intc_getvicirqstatus(i) != 0){isr = (void (*)(void)) vicaddr[i];break;}}(*isr)(); // 通過函數指針來調用函數 }整個中斷的流程梳理:
整個中斷的工作分為2部分:
第一部分是我們為中斷響應而做的預備工作:
1. 初始化中斷控制器2. 綁定寫好的isr到中斷控制器3. 相應中斷的所有條件使能第二部分是當硬件產生中斷后如何自動執行isr:
1. 第一步,經過異常向量表跳轉入IRQ/FIQ的入口2. 第二步,做中斷現場保護(在start.S中),然后跳入isr_handler3. 第三步,在isr_handler中先去搞清楚是哪個VIC中斷了,然后直接去這個VIC的ADDR寄存器中取isr來執行即可。4. 第四步,isr執行完,中斷現場恢復,直接返回繼續做常規任務。總結
以上是生活随笔為你收集整理的S5PV210裸机之外部中断的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 【知识】人工智能数学基础知识
- 下一篇: 大数据杀熟!用苹果手机怪我咯?