新聞中心

        EEPW首頁 > 模擬技術 > 設計應用 > Xtensa處理器窗寄存器函數調用機制與應用

        Xtensa處理器窗寄存器函數調用機制與應用

        作者: 時間:2012-10-10 來源:網絡 收藏

        本文引用地址:http://www.104case.com/article/185680.htm

          5. Windows下溢(underflow)問題

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

          如果不是全為零,則應該不為零的點和正常window返回的點對應,要正常返回,如果不同,則說明發生了不正常的調用,a0被破壞掉,要產生非法指令錯誤。關于這個方面的具體硬件原語,讀者可以參考的ISA手冊,這里不再贅述。

        Alloca異常問題

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

          當函數內部進行局部stack的內存分配時,編譯器會生成一個MOVSP at,as指令,異常的檢測通過這一指令來完成,該指令有如下原語:

          if WindowStartWindowBase-0011WindowBase-0001 = 03 then

          Exception (AllocaCause)

          elseAR[t] ← AR[s]

          endif

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

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

          調用alloc函數,如

          void foo(int array_size) {

          char * bar = alloca(array_size);

          …

          使用變長數組(GNU C 擴展語言),如

          void foo(int array_size) {

          char bar[array_size];

          …

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

          void afunction(void) {

          …

          int anotherfunction(void) {

          }

          使用特別長的局部數組,如

          void foo(void) {

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

          int another_array[100]; // 400 bytes

          …

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

          當然,這里列出的不是全部的可能情況,僅僅列出幾個常見用例,讀者不能認為自己的代碼沒有以上情況,編譯器就不會產生movsp指令,從而不會產生alloc異常。

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

          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

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

          Context Switch下的寄存器保存與恢復

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

          對于任務軟件來說,寄存器的實現機制是透明的,因此特別要說明的是WindowsStart和WindowsBase寄存器則完全不需要保存與恢復。參考如下進程切換的環境保護核心代碼:

          .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窗口的相對偏移,基于調用棧布局規范進行入棧處理。 當任務切換到其他的進程后,這些live窗口可能會被破壞(相應的WindowStart比特清零),這沒有關系, 當進程重新切換回來時,如果這些live窗口已經在切換過的進程恢復(WindowStart比特置1),則切換回來后無需出棧,程序可正常繼續執行,如果沒有恢復(相應的WindowStart比特繼續為零),那么該進程就可以根據這個清零的狀態位將原先入棧的live寄存器正確恢復。

          本文小結

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

        c語言相關文章:c語言教程



        上一頁 1 2 下一頁

        評論


        相關推薦

        技術專區

        關閉
        主站蜘蛛池模板: 靖远县| 融水| 晴隆县| 台江县| 柳河县| 玉溪市| 民和| 滨海县| 桃源县| 开平市| 永城市| 济宁市| 南木林县| 静安区| 出国| 麟游县| 崇义县| 虎林市| 房产| 神农架林区| 银川市| 宣恩县| 英超| 德庆县| 金秀| 龙海市| 泸水县| 江阴市| 平顶山市| 根河市| 绍兴县| 白玉县| 葵青区| 邯郸县| 丹东市| 长岭县| 彩票| 贵德县| 信宜市| 祁阳县| 岱山县|