新聞中心

EEPW首頁 > 模擬技術(shù) > 設(shè)計(jì)應(yīng)用 > Xtensa處理器窗寄存器函數(shù)調(diào)用機(jī)制與應(yīng)用

Xtensa處理器窗寄存器函數(shù)調(diào)用機(jī)制與應(yīng)用

作者: 時(shí)間:2012-10-10 來源:網(wǎng)絡(luò) 收藏

本文引用地址:http://2s4d.com/article/185680.htm

  5. Windows下溢(underflow)問題

  當(dāng)子返回時(shí),RETW或者RETW.N指令執(zhí)行,此時(shí)也僅此時(shí)將進(jìn)行上溢檢查。如果當(dāng)Windowbase所在位置的前3個(gè)window pane(4 registers組)的WindowStart比特都為零,則意味著返回后的父發(fā)生過 WindowOverflow,父的窗口曾經(jīng)被壓入棧,要先行通過相應(yīng)的underflow彈出。

  如果不是全為零,則應(yīng)該不為零的點(diǎn)和正常window返回的點(diǎn)對(duì)應(yīng),要正常返回,如果不同,則說明發(fā)生了不正常的調(diào)用,a0被破壞掉,要產(chǎn)生非法指令錯(cuò)誤。關(guān)于這個(gè)方面的具體硬件原語,讀者可以參考的ISA手冊(cè),這里不再贅述。

Alloca異常問題

  C語言中函數(shù)中經(jīng)常會(huì)發(fā)生從堆棧中分配臨時(shí)空間的情況,在正常的不發(fā)生窗溢出的時(shí)候沒有任何問題。但是,如果該函數(shù)的下級(jí)函數(shù)的嵌套調(diào)用曾導(dǎo)致過寄存器溢出,由于該函數(shù)的堆棧Frame底部存有溢出的basic area的寄存器,如果簡(jiǎn)單的偏移堆棧指針來分配臨時(shí)空間,則這些保存過basic寄存器會(huì)被完全破壞掉。為有效解決這一問題,架構(gòu)引入一個(gè)特殊的Alloca異常來管理basic area寄存器的搬移和臨時(shí)空間的分配。

  當(dāng)函數(shù)內(nèi)部進(jìn)行局部stack的內(nèi)存分配時(shí),編譯器會(huì)生成一個(gè)MOVSP at,as指令,異常的檢測(cè)通過這一指令來完成,該指令有如下原語:

  if WindowStartWindowBase-0011WindowBase-0001 = 03 then

  Exception (AllocaCause)

  elseAR[t] ← AR[s]

  endif

  類似于underflow,如果當(dāng)前寄存器窗口前3個(gè)register pane的占用狀態(tài)全為0(全部為自由使用狀態(tài)),則說明其上一級(jí)函數(shù)一定發(fā)生過窗口溢出,當(dāng)前函數(shù)棧下方一定保存有溢出的寄存器,簡(jiǎn)單的修改SP指針不再安全,需要觸發(fā)Alloca異常來進(jìn)行正確處理。需要說明的是,發(fā)生alloc異常的時(shí)候,過去的寄存器窗口調(diào)用已經(jīng)循環(huán)一周,且發(fā)生溢出,溢出的充分必要條件必然是當(dāng)前寄存器窗口的前3個(gè)register pane占用狀態(tài)全為0(WindowStartWindowBase-0011WindowBase-0001 = 000),其次當(dāng)前函數(shù)不可能是調(diào)用樹的葉子節(jié)點(diǎn),當(dāng)前函數(shù)的前半部分曾經(jīng)進(jìn)入過,且過去進(jìn)入的路徑上發(fā)生過溢出,否則就沒有產(chǎn)生異常的必要。alloca異常是為解決sp覆蓋而引入的硬件機(jī)制。

  這里解釋了alloc異常產(chǎn)生的基本原理,那么,什么樣的代碼會(huì)產(chǎn)生MOVsp指令,從而可能觸發(fā)alloc異常呢? 有如下幾種情況:

  調(diào)用alloc函數(shù),如

  void foo(int array_size) {

  char * bar = alloca(array_size);

  …

  使用變長(zhǎng)數(shù)組(GNU C 擴(kuò)展語言),如

  void foo(int array_size) {

  char bar[array_size];

  …

  使用嵌套函數(shù)定義(GNU C 擴(kuò)展語言),如

  void afunction(void) {

  …

  int anotherfunction(void) {

  }

  使用特別長(zhǎng)的局部數(shù)組,如

  void foo(void) {

  int an_array[8192]; // 32,768 bytes

  int another_array[100]; // 400 bytes

  …

  精確的size限制是32,760,包含16~48字節(jié)的Frame開銷。

  當(dāng)然,這里列出的不是全部的可能情況,僅僅列出幾個(gè)常見用例,讀者不能認(rèn)為自己的代碼沒有以上情況,編譯器就不會(huì)產(chǎn)生movsp指令,從而不會(huì)產(chǎn)生alloc異常。

  由于alloc是一種不容易避免的正常的異常,應(yīng)用軟件需要積極的處理。處理的思路有兩種,其一是用異常或者中斷棧作為臨時(shí)儲(chǔ)存來搬移,這里要介紹另外一種比較巧妙的方法,如下述代碼:

  rsr a2,PS

  rsr a4,WINDOWBASE

  extui a3,a2,XCHAL_PS_OWB_SHIFT,XCHAL_PS_OWB_BITS

  xor a3,a3,a4

  slli a3,a3,XCHAL_PS_OWB_SHIFT

  xor a2,a2,a3

  wsr a2,PS

  l32i a4,a1,A4_SAVE

  l32i a3,a1,A3_SAVE

  l32i a2,a1,A2_SAVE

  addi a1,a1,USER_EXCEPTION_FRAME_SIZE

  rsync

  // Now branch on the call increment

  // We could branch earlier rotw instructions prior to the handler

  // which would avoid executing two rotw instructions in the underflow

  // eight case. However,the underflow 8 handler is right at a

  // cache-line,so that would likely involve an extra cache miss better

  // to just take the single cycle penalty here.

  rotw -1

  // what was a0 (that had the return address) is now a4

  _bbci.l a4,31,_WindowUnderflow4

  rotw -1

  // what was a0 (that had the return address) is now a8

  _bbci.l a8,30,_WindowUnderflow8

  j _setup_WindowUnderflow12

  這段代碼的巧妙之處在于首先通過將當(dāng)前的Windowbase保存到PS的OWB域中,然后通過反向旋轉(zhuǎn)窗,根據(jù)a0的高端2bit表示的調(diào)用類型,跳轉(zhuǎn)到相應(yīng)的下溢exception的位置進(jìn)行出棧恢復(fù)save area的寄存器,當(dāng)sp指針正確偏移后,利用寄存器的引用觸發(fā)overflow異常自動(dòng)進(jìn)行再次入棧,從而實(shí)現(xiàn)搬移。

  Context Switch下的寄存器保存與恢復(fù)

  在windows窗口寄存器機(jī)制下,多任務(wù)RTOS的上下文環(huán)境切換問題變得非常有趣。 那么該如何保存這些通用寄存器呢,是不是所有的物理寄存器都要保存與恢復(fù)呢? 答案是否定的,除了當(dāng)前窗口的邏輯寄存器要保存外,只需要保存當(dāng)前任務(wù)進(jìn)程里的live寄存器(置1的寄存器pane),而恢復(fù)則只需要恢復(fù)邏輯寄存器。

  對(duì)于任務(wù)軟件來說,寄存器的實(shí)現(xiàn)機(jī)制是透明的,因此特別要說明的是WindowsStart和WindowsBase寄存器則完全不需要保存與恢復(fù)。參考如下進(jìn)程切換的環(huán)境保護(hù)核心代碼:

  .Lspill_loop:

  // Top of save loop.

  // Find the size of this call and branch to the appropriate save routine.

  beqz a2,.Ldone // if no start bit remaining,we're done

  bbsi.l a2,0,.Lspill4 // if next start bit is set,it's a call4

  bbsi.l a2,1,.Lspill8 // if 2nd next bit set,it's a call8

  bbsi.l a2,2,.Lspill12 // if 3rd next bit set,it's a call12

  j .Linvalid_window // else it's an invalid window!

  // SAVE A CALL4

  .Lspill4:

  addi a3,a9,-16 // a3 gets call[i+1]'s sp - 16

  s32i a4,a3,0 // store call[i]'s a0

  s32i a5,a3,4 // store call[i]'s a1

  s32i a6,a3,8 // store call[i]'s a2

  s32i a7,a3,12 // store call[i]'s a3

  srli a6,a2,1 // move and shift the start bits

  rotw 1 // rotate the window

  j .Lspill_loop

  // SAVE A CALL8

  .Lspill8:

  ……

  該代碼的基本思路是通過處理WindowStart寄存器,解析各live窗口的相對(duì)偏移,基于調(diào)用棧布局規(guī)范進(jìn)行入棧處理。 當(dāng)任務(wù)切換到其他的進(jìn)程后,這些live窗口可能會(huì)被破壞(相應(yīng)的WindowStart比特清零),這沒有關(guān)系, 當(dāng)進(jìn)程重新切換回來時(shí),如果這些live窗口已經(jīng)在切換過的進(jìn)程恢復(fù)(WindowStart比特置1),則切換回來后無需出棧,程序可正常繼續(xù)執(zhí)行,如果沒有恢復(fù)(相應(yīng)的WindowStart比特繼續(xù)為零),那么該進(jìn)程就可以根據(jù)這個(gè)清零的狀態(tài)位將原先入棧的live寄存器正確恢復(fù)。

  本文小結(jié)

  Xtensa的寄存器窗口旋轉(zhuǎn)函數(shù)調(diào)用是一種非常巧妙的實(shí)現(xiàn)機(jī)制,通過這一機(jī)制嵌入式軟件可明顯提高性能,并且其alloc異常,多任務(wù)上下文切換等等衍生和應(yīng)用問題也可高效而經(jīng)濟(jì)的解決,其和TIE(Tensilica Instruction Extension),其他諸多可配置選項(xiàng)等等正充分說明了Xtensa架構(gòu)經(jīng)久不衰,廣泛應(yīng)用,煥發(fā)持久生命力的原因所在。

c語言相關(guān)文章:c語言教程



上一頁 1 2 下一頁

關(guān)鍵詞: Xtensa 處理器 寄存器 函數(shù)

評(píng)論


相關(guān)推薦

技術(shù)專區(qū)

關(guān)閉