1. 程式人生 > >讀書筆記之ARM相關篇

讀書筆記之ARM相關篇

ARM處理器啟動過程

ARM處理器的上電和復位操作過程類似,都是從處理器的低端復位向量地址0的位置讀取第一條指令,由於ARM處理器的異常向量地址是連續排列的,所以異常中端入口指令一般都是存放的一條跳轉指令。跳轉到哪裡呢?至少要跳轉過異常中端向量區,然後按照程式流來執行。

ARM處理器系統初始化過程

每次系統上電覆位啟動時,處理器都處於一種最低效能的基本功能狀態,此時,只能從復位向量地址去取指令,其他的功能都處於禁止狀態。一旦處理器讀取到第一條指令開始執行,那麼從軟體的角度來講,處理器和整個系統都處於軟體可控的狀態。系統的整個流程將由軟體來決定。

系統初始化的一般順序為:

  1. 禁止MMU,關閉中斷和cache
  2. 根據硬體配置好處理器的時鐘,DRAM的時鐘,定時器的時鐘
  3. 根據系統中所有的Flash和DRAM晶片的容量和電器引數設定他們的起始地址,容量和重新整理頻率等引數。
  4. 將固化在FLASH中的程式搬到DRAM中執行
  5. 使能cache,MMU,跳轉到記憶體中繼續執行初始化程式,包括具體的應用以及系統中的硬體的初始化各個功能模組。安裝好中斷處理程式,開啟中斷。
  6. 進行系統相關的初始化

ARM處理器系統初始化過程注意事項

在上述的初始化過程中有兩點需要注意,如果處理不好就會出現處理器跑飛的現象

  1. 如果需要將處理器的程式碼從Flash搬到DRAM中執行,由於我們在編譯階段,編譯和連結的程式都是將程式連結到程式最後執行的場所DRAM中的地址上,但是系統啟動的時候,都是從Flash中讀取第一條指令 ,所以從系統啟動讀取的第一條指令到跳轉到DRAM之前執行的程式碼都是與位置無關的程式碼,如果要跳轉到某一個標號處去執行,必須是基於PC的,不能是絕對的跳轉指令,因為此時程式存放的位置與其連結到的位置不同,如果使用絕對跳轉指令,程式將跑非。
  2. 如果要求程式在MMU使能狀態下執行,即系統工作在虛擬地址模式,在使用MMU的系統中各種實體地址儲存空間的時間實體地址與對應的虛擬地址設定的各不相同(包括Flash和DRAM實體地址與虛擬地址的攝影關係不同)。系統啟動時,MMU被禁止,系統執行在真實模式下,即使用的都是實體地址,但是程式被編譯連結到了虛擬地址上,這樣在MMU使能之前存放在Flash或者DRAM中的程式的實際的實體地址與其連結的虛擬地址是不同的,所以從系統啟動到MMU被使能這期間的程式碼也應該是與地址無關的程式碼,如果要跳轉到某一個標號的位置去執行,也同樣是基於PC的,不能是絕對的跳轉指令,否則程式一樣會跑非。

ARM處理器的工作模式與異常中斷處理

ARM處理器的工作模式

ARM處理器共有七種工作模式,除了使用者模式之外,其他工作哦模式都可以稱之為特權模式。在特權模式下,處理器可以訪問系統下的所有資源。也可以任意的切換模式。特權模式中,除了system模式之外,其他的五種模式又稱為異常模式。

處理器模式可以通過軟體控制進行切換,也可以通過外部中端或者內部異常處理程式來進行切換。執行在使用者模式下的程式,不能訪問一些受作業系統保護的資源。也不能直接進行處理器模式的切換。如果想要進行模式的切換,可以產生異常處理,在異常處理過程中進行模式的切換,這種體系結構可以使作業系統控制整個系統的資源。每一種異常模式中,都有一組暫存器供異常處理器程式使用,這樣可以保證在進入異常模式時,使用者模式下的暫存器不被破壞。

系統模式不是通過異常過程進入的,它和使用者模式擁有完全相同的暫存器,系統模式屬於特權模式,可以訪問所有的系統資源。也可以直接進行處理器的模式切換,主要是給作業系統核心使用的模式。通常作業系統核心需要訪問所有的資源,同時該程序仍然使用使用者模式下的暫存器,而不是異常模式下的暫存器,這樣可以保證當異常發生的時候,核心程序不被破壞。

以下是ARM處理器的工作模式:

  1. 使用者模式(USER)
  2. 系統模式(SYSTEM)
  3. 超級使用者模式(Supervisor)
  4. 快速中斷模式(FIQ)
  5. 外部中斷模式(IRQ)
  6. 資料訪問終止模式(Data Abort)
  7. 未定義模式(Undefined)

ARM處理器的中斷向量和優先順序

異常:是指由處理器執行指令導致的程式的終止,異常與指令執行相關,是CPU執行程式所產生的,是同步的。其中異常可分為精確異常和非精確異常,異常處理程式遵循嚴格的程式執行順序,不能巢狀(中斷可以),只有當第一個異常處理完之後,才能繼續處理下面的異常。

中斷:是屬於異常的一種,但是中斷不是由於CPU的程式所引起的,而是外部硬體所觸發的,是非同步的。

下面是ARM處理器的異常向量表和對應的優先順序

低端向量地址                     名稱                            系統模式

0x00000000                    復位                            使用者模式

0x00000004                    未定義指令終止                  未定義指令模式

0x00000008                     軟中斷(swi)                  超級使用者模式

0x0000000c                     Prefetch abort                 資料訪問終止

0x00000010                     Data abort                    資料訪問終止

0x00000014                     Reserved                      Reserved

0x00000018                     IRQ                            外部中斷模式

0x0000001c                     FIQ                            快速中斷模式

說明:

上面的低端向量地址的高4位變成ffff,低4位不變的地址就是高階向量地址,高階向量是ARM架構的可選配置,可以通過硬體的外部輸入管腳來配置系統採用低端向量還是高階向量,不能通過指令來改變向量的地址。但是如果ARM晶片內部有標準的協處理器,那麼協處理器CP15的暫存器C1的bit13可以用來切換低端向量還是高階向量,bit13為0,表示低端向量,為1表示高階向量。

ARM處理器的異常中斷處理

主要介紹進入異常中斷處理程式都做了什麼,和退出的時候都做了什麼?

  1. 進入異常中斷處理

ARM處理器發生異常中斷,則處理器進入如下異常中斷自動處理過程(假設發生的異常中斷對應的模式為mode)

A:將當前程式狀態暫存器CPSR的值儲存到SPSR_mode中

B:將CPSR中模式位設定城mode模式,將CPSR中的bit7設定為1(禁止中斷IRQ),如果是FIQ中斷則再將CPSR中的bit6設定為1(禁止FIQ中斷)

C:將返回地址傳給lr_mode

D:將該異常向量的地址傳送給程式計數器PC,從而進入異常中斷處理程式。

  1. 退出異常中斷處理程式

當要從異常中斷處理程式返回的時候,要做以下兩步操作(假設發生的異常中斷對應的模式為mode)

A:將儲存在SPSR_mode的值回覆到當前程式狀態暫存器CPSR中

B:返回到異常中斷指令的下一條指令處開始執行,也就是將lr_mode暫存器的值適當地址返回到程式計數器pc中。

軟體人員只需要做好上述第二步就好,第一步是由處理器自動完成的。

ARM處理器儲存訪問的一致性問題

什麼是儲存訪問一致性?

當儲存系統中引入了cache和write buffer的時候,同一地址單元的資料可能在系統中有多個副本,記憶體中有一份,cache和write buffer中各有一份,如果系統中採用了獨立的資料cache和指令cache,同一地址單元的資料還可能在資料cache和指令cache中有不同的版本。位於不同物理位置的統一地址單元的資料可能不同,使得資料讀操作可能不是系統中“最新的”資料,這樣帶來了儲存訪問一致性的問題。在ARM儲存系統中,資料不一致的問題需要通過程式設計時遵守一定的規則來避免不一致的問題。

  1. 地址對映關係變化造成的資料不一致

當系統中使用了MMU時,就建立了虛擬地址到實體地址的對映關係,如果查詢cache的時候進行的相連比較使用的是虛擬地址,則當系統中虛擬地址到實體地址的對映關係發生變化的時候,可能造成cache中的資料和記憶體中的資料不一致的情況。在虛擬地址到實體地址的對映關係變化之前,如果虛擬地址A1所在的資料塊已經被預取到cache中,當虛擬地址到實體地址的對映關係發生變化後,如果虛擬地址A1對應的實體地址發生了改變,則CPU訪問A1時,再使用cache中的資料就會得到錯誤的資料。同樣系統使用的是write buffer,系統向虛擬地址A1寫資料的時候,並沒有真正的將資料寫入到記憶體,而是寫入到了write buffer中,一段時間之後,write buffer會自動向記憶體總寫入,如果在write buffer向記憶體寫入資料之前,虛擬地址A1對映到實體地址的位置發生變化,則write buffer中的資料將被寫入到變化後的實體地址上,會造成資料錯誤。

為了避免這種資料不一致的情況發生,在系統中的虛擬地址和實體地址變化之前根據系統的情況執行下面的操作中的一種或者幾種。

A:如果資料cache為write buffer型別,清空該資料的cache

B:使cache中相對應的資料塊無效

C:將write buffer中被延遲的寫操作提前執行

D:有些情況需要將儲存區域設定城非緩衝的

  1. 指令cache的資料不一致的問題

當系統中採用獨立的資料cache和獨立的指令cache的時候,下面的操作序列可能造成指令不一致的問題:

A:讀取地址為A1的指令,從而包含該指令的資料塊被預取到指令cache中

B:與A1在同一個資料塊中的地址為A2的儲存單元的資料被修改,這個資料寫操作可能影響資料cache write buffer和記憶體中地址為A2的儲存單元的內容,但是不影響指令cache中地址為A2的儲存單元的內容。

C:地址A2存放的是指令,當該指令執行的時候就可能發生指令不一致的問題,如果地址A2所在的塊還是在指令cache中,系統將執行修改前的指令,如果地址A2所在的塊不在指令cache中,系統將執行修改後的指令。

為了避免指令不一致的問題,在上面的A和B之間插入下面的操作序列:

A:對於使用統一的資料cache和指令cache的系統,不需要任何操作

B:對於使用獨立的資料cache和指令cache的系統,需要使指令cache的內容無效

C:對於使用獨立的資料cache和指令cache的系統,如果資料cache是write back型別的,清空資料cache,當資料操作修改了指令的時候,最好執行上面的操作序列,保證指令的一致性。當可執行檔案載入到記憶體後,在程式執行到入口點執行之前,先執行上面的操作序列,就能夠保證下面指令是新載入的可執行檔案的程式碼,而不是指令中原來的舊程式碼。

3)DMA造成的資料不一致問題

DMA操作直接訪問記憶體,而不會更新cache和write buffer中對應的資料。這樣很可能產生資料不一致的問題。如果DMA從記憶體中讀取的資料已經包含在cache中,而且cache中對應的資料已經被更新,這樣DMA讀到的將不是系統中最新的資料。同樣DMA寫操作直接更新記憶體中的資料,如果該資料已經包含在cache中,則cache中的資料將會比記憶體中的資料老,也將產生資料不一致的問題。為了避免這種情況發生,根據系統中的情況,執行下面的操作中的一種或幾種:

A:將DMA訪問的記憶體設定城uncached或者unbuffered

B:將DMA訪問的記憶體所涉及的資料在cache中的塊無效,或者清空cache

C:清空write buffer(執行wite buffer中延遲的所有寫操作)

D:在DMA操作期間限制處理器訪問DMA所訪問的區域。

Linux中解決儲存訪問一致性的問題的方法

在linux中用巨集barrier()來解決儲存訪問的一致性問題,barrier()的定義如下所示:

#define barrier() __asm__ __volatile__(“”:::”memory”)

另外在barrier()的基礎上還衍生出了很多類似的定義,如:

#define mb()   __asm__ __volatile__(“”:::”memory”)

#define rmb() mb()

#define wmb() mb()

#define smp_mb() barrier()

#define smp_rmb() barrier()

#define smp_wmb() barrier()

Barrier是記憶體遮蔽的意思,CPU越過記憶體屏障後,將重新整理自己對暫存器的緩衝狀態,barrier()巨集定義這條語句實際上不生成任何程式碼,但是在gcc編譯的時候,在barrier()後重新整理暫存器對變數的分配具體分析如下:

__asm__:告訴編譯器下面的程式碼是彙編程式碼

__vilatile__:告訴編譯器該物件可能在程式之外被修改,嚴禁將此處的彙編語句與其他的語句重組和優化,即按照原來的樣子處理這裡的彙編。在C語言中通過使用關鍵字volatile宣告儲存器對映的I/O空間來防止編譯器在優化時刪掉有用的儲存訪問操作。

Memory:告訴編譯器所有記憶體單元都被彙編指令修改過,registers和cache中的已經快取的資料將被作廢,barrier()之後的程式CPU必須重新從記憶體中讀取資料而不是使用過期的registers和cache中的資料。

“”:::表示這是個空指令,barrier()不用在此插入一條串化行彙編指令,所謂序列化指令也就是同步指令,即將cache,write buffer和記憶體中的資料同步更新。

總的來說barrier()有兩個作用:

第一:告訴編譯器不要優化這部分程式碼,保持程式碼原來的順序執行。

第二:告訴CPU執行完barrier()的程式碼後要進行同步操作,更新registers和cache以及寫快取和記憶體中的內容,使用資料全部從記憶體中重新讀取。