新聞中心

        EEPW首頁 > 嵌入式系統 > 設計應用 > MAXQ微控制器架構的表操作

        MAXQ微控制器架構的表操作

        ——
        作者: 時間:2007-01-26 來源:《Maxim》 收藏

        簡介

        MAXQ 架構是一種基于標準Harvard結構、功能強大的單周期RISC微控制器。Harvard結構與常見的Von Neumann結構相比,其不同之處在于重要的設計結構方面:Harvard結構的指令與數據在不同的總線上傳輸。由于不存在單條數據總線的沖突問題,MAXQ指令的執行時間僅需要單個周期。而傳統的Von Neumann架構完成相同的操作則需要多個周期。

        然而,Harvard結構中數據與代碼的嚴格分離也帶來了一系列的挑戰。Von Neumann結構的一項通用技術就是可以在代碼空間存儲數據表,這對于標準Harvard結構來說是很難實現的。在給定總線上,單個指令周期內只能進行一個操作,因此在同一周期內,CPU核不可能既從代碼存儲器總線上取指令,又從代碼空間的數據表中取出存儲器操作數。

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

        有人可能會認為采用Harvard結構的MAXQ微控制器也不能在代碼空間內存儲數據。實際上,每一款MAXQ器件都內嵌了ROM工具,因此很容易實現表項查找操作。


        代碼空間的表查找

        從代碼空間的MAXQ表中讀取一個數值看似簡單,然而對于不熟悉MAXQ架構的編程人員來說,第一次嘗試該操作時通常會失敗。

        IncorrectTableLookup:
        move dp[0], #w:StartOfTable
        move acc, @dp[0]
        .
        .
        .
        ret
        .
        .
        .
        StartOfTable:
        dc16 01234h
        dc16 05678h
        dc16 098abh
        dc16 0cdefh

        上述代碼能很順利地完成匯編,但是執行完第二條指令之后,累加器的值幾乎不可能是0x1234。原因很簡單,Von Neumann結構只有一個單獨的存儲空間,一條指令根據操作的需要可能花費多個指令周期,而MAXQ與此不同,其move , @dp[0]指令在單個周期內隱含地訪問數據空間并完成指令操作,即從代碼空間取指令,并從數據空間讀取數據。裝載到累加器中的數值是存儲于數據空間的數據,只不過該數據的偏移量和代碼空間的StartOfTable相同。

        這個問題剛開始似乎很難解決。畢竟,訪問代碼空間需要一定的時間;CPU核不能將兩次存儲器訪問壓縮在一個時鐘周期內完成,即使架構允許這樣。然而,如果我們了解了MAXQ架構的微控制器如何將物理存儲模塊映射到不同存儲空間的一些細節信息,并借助于固定用途ROM中的一些程序,就可以解決這一問題。

        首先,在MAXQ架構中,將物理存儲模塊映射至代碼空間和數據空間的方式不是固定的,而是取決于正在訪問的物理存儲模塊。編程人員為大多數MAXQ微控制器所編寫的代碼都運行于閃存空間內,通常他們將其軟件連接到代碼空間的地址0處。編程人員會認為RAM也是從數據空間的地址0開始的,事實也的確如此。

        但是,MAXQ微控制器還有另一塊物理存儲器,即固定用途ROM。所有MAXQ微控制器的固定用途ROM都位于代碼空間的地址0x8000。用戶代碼可以調用固定用途ROM中0x8000頁面的程序,執行特定的函數。并且,只要執行固定用途ROM中的程序,用戶代碼存儲器即被重新映射到數據空間的一個新地址上。

        開始執行固定用途ROM的程序后,可以繼續訪問數據空間以地址0x0000開始的數據RAM,而代碼存儲器卻被重新映射到數據空間以地址0x8000開始的位置。因為代碼閃存映射到了數據空間,運行的固定用途ROM代碼可以像訪問數據一樣訪問存儲于用戶代碼中的數據信息。通過固定用途ROM函數,可簡單地通過指針寄存器間接讀取數據并返回結果。 因此,將上面給出的程序稍作改動后得到:


        BetterTableLookup:
        move dp[0], #w:StartOfTable + 08000h
        call UtilityROMGetDP0
        .
        .
        .
        ret
        .
        .
        .
        StartOfTable:
        dc16 01234h
        dc16 05678h
        dc16 098abh
        dc16 0cdefh

        在本例程中,調整待讀取的地址,以反映執行固定用途ROM程序時閃存的映射地址,然后將其裝入DP[0]。這里采用了調用固定用途ROM程序的方法,而不是直接讀取數據。當然,直接讀取數據只占用一個指令周期,而這一操作則占用了四個指令周期:2個周期用于長調用,1個周期用于讀取數據,1個周期用于返回操作。

        這個代碼例程存在的更大問題是不能進行匯編操作!標記UtilityROMGetDP0沒有定義,造成這一結果的原因很簡單:各款MAXQ微控制器的實用程序地址是不同的。事實上,甚至不能保證這些程序在特定MAXQ器件的不同版本中位于相同的位置!

        為解決這一問題,每一款MAXQ微控制器的固定用途ROM都包含一個固定用途函數的地址表,以及一個指向該表的指針,該指針的地址固定為0x800D。需明確指出的是,固定用途ROM包含以下代碼:


        org 0800Dh
        dw UtilityFunctionTable
        .
        .
        .
        UtilityFunctionTable:
        .
        .
        .
        dw GetDP0
        dw GetDP0Inc
        dw GetDP0Dec
        dw GetDP1
        dw GetDP1Inc
        dw GetDP1Dec
        dw GetBP
        dw GetBPInc
        dw GetBPDec

        注意:第一,固定用途函數表由地址組成,而不是指令。因此,編程人員必須提取地址并call它,而不能簡單地跳轉至該表。第二,第一個存儲器函數也許不是該表的入口。由于每款MAXQ微控制器包含不同類型和容量的存儲器以及不同的外設,每款器件很有可能包含不同的函數列表,函數在表中具有不同的相對偏移量。例如,MAXQ3120在第1個表查找程序之前,有3個與閃存編程有關的固定用途程序。

        3個指針寄存器各自都有3個相關的函數,總計有9個表查找函數。每個指針寄存器的第一個函數只是提取位于給定地址的數據,而后兩個函數分別采用后遞增和后遞減形式的間接裝載。在每一種情況下,都將提取到的數據裝載到GR寄存器中。

        現在,代碼可改為如下形式:


        CorrectTableLookup:
        move dp[0], #0800Dh ; Point to pointer to function table
        move acc, @dp[0] ; acc now has pointer to ftable
        add #3 ; For MAXQ3120 and 2000, GetDP0
        move dp[0], acc ; Load ptr + offset to dp0
        move a[1], @dp[0] ; Get address of GetDP0 into A1
        move dp[0], #StartOfTable + 08000h
        call a[1] ; This will call GetDP0, finally!
        .
        .
        .
        ret
        .
        .
        .
        StartOfTable:
        dc16 01234h
        dc16 05678h
        dc16 098abh
        dc16 0cdefh

        需注意,一旦找到GetDP0程序的地址,即可將該地址存放起來并重復使用。上述前5條指令只需要執行一次;然后,每次訪問表數據操作只需要三個指令周期:調用,讀取(運行固定用途ROM內的程序),返回(也運行固定用途ROM內的程序)。


        將數據表從閃存拷貝到RAM
        將整個表從閃存拷貝到RAM的方法之一是利用表的讀函數實現。例如,如果在BP中給出了目標地址,那么拷貝方法如下:

        SlowTableMove:
        move dp[0], #0800Dh ; Point to pointer to function table
        move acc, @dp[0] ; acc now has pointer to ftable
        add #4 ; For MAXQ3120 and 2000, GetDP0Inc
        move dp[0], acc ; Load ptr + offset to dp0
        move a[1], @dp[0] ; Get address of GetDP0 into A1
        move dp[0], #StartOfTable + 08000h
        move bp, #RAMDest ; Set this label to desired dest
        move offs, #0ffh ; Pre-decremented offset
        move lc[0], #4 ; Move four words
        TableMoveLoop:
        move dp[0], dp[0] ; Set source pointer
        call a[1] ; This will call GetDP0inc
        move @bp[++offs], gr ; Store retrieved word to dest
        djnz lc[0], TableMoveLoop
        .
        .
        .
        ret
        .
        .
        .
        StartOfTable:
        dc16 01234h
        dc16 05678h
        dc16 098abh
        dc16 0cdefh

        如上文所述,前5條指令只需要執行一次,此后可根據需要多次執行表操作,GetDP0inc子程序地址始終保存在A1中。每次執行表操作需要6個指令周期外加建立開銷。

        加入move dp[0], dp[0]指令是MAXQ架構的特殊性要求的。由于數據空間只有一條地址總線,因此必須在讀數據空間操作的前1個周期先將地址建立起來。在表操作循環中,對DP[0]給出的地址進行讀操作,然后在總線上放置寫地址。如果沒有move dp[0], dp[0]指令,當讀取表中下一個地址的數據時,寫地址會仍然占據總線。通過插入這條明顯的空指令,可以為預期的下一個讀操作刷新源操作數地址總線。

        然而,還有一個更好的方法完成該拷貝任務。固定用途ROM中包括一個能實現上述相同功能的copyBuffer程序,而且所占用的指令周期更少。copyBuffer程序在固定用途ROM中位于表查找程序的后面。


        FasterTableMove:
        move dp[0], #0800Dh ; Point to pointer to function table
        move acc, @dp[0] ; acc now has pointer to ftable
        add #12 ; For MAXQ3120 and 2000, copyBuffer
        move dp[0], acc ; Load ptr + offset to dp0
        move a[1], @dp[0] ; Get address of GetDP0 into A1
        move dp[0], #StartOfTable + 08000h
        move bp, #RAMDest ; Set this label to desired dest
        move offs, #0 ; No need to pre-decrement offset
        move lc[0], #4 ; Move four words
        call a[1] ; This will call copyBuffer
        .
        .
        .
        ret
        .
        .
        .
        StartOfTable:
        dc16 01234h
        dc16 05678h
        dc16 098abh
        dc16 0cdefh

        copyBuffer程序將每次表操作的周期數減至3個,比之前提到的方法節省了約一半時間。當從copyBuffer程序返回時,LC[0]清零,OFFS寄存器指向最近一次寫目標地址的下一個位置。因為OFFS是一個8位寄存器,因此用這種方法可以拷貝多達256字的表。


        實例:字符串輸出
        在許多基于微控制器的應用中,通常都要將預存的消息輸出到控制臺。每條消息都指定了一個編號,必須由一個通用程序將該編號轉換成消息文本。
        完成該任務通常采用每個消息字符串以0結尾的技術,同時提供一個表,以便將各消息編號轉換成消息字符串的首地址。這項技術非常可靠和快速,但必須建立兩個數據結構:地址表及字符串本身。另一項技術是簡單地將以0結尾的各字符串存入一個大的、毗鄰的存儲器空間,并采用線性查找。雖然該方法比較簡單,但卻是以花費大量執行時間為代價的,因為在輸出之前,必須找到目標字符串里的每一個字符。

        還有一種較好的折衷辦法,即字符串采用按長度劃界的方法取代以0劃界的方法。采用這種技術,首先給出每個字符串的長度,然后緊接著是該消息的實際字節信息。這樣一來,可以快速跳過不用的信息,并且該表的長度沒有以0劃定界限的長。這種折衷技術的局限性僅在于表中的每個字符串長度不能超過255個字符。


        ;
        ; Output String
        ;
        ; Enter with ACC=an index value (one based) indicating which
        ; string to output.
        ;
        ; On exit, LC0=0, DPC=0, ACC, A1, A2, DP0 used.
        ;
        output_string:
        move lc[0], acc ;Set LC0 to index of string
        move dpc, #4 ;Set DP0 to word mode
        move dp[0], #800dh ;Point to table of pointers
        move acc, @dp[0] ;Get address of table
        add #3 ;Offset to GETDP0 routine
        move dp[0], acc ;Load pointer to table
        move a[1], @dp[0]++ ;Get GETDP0
        move a[2], @dp[0] ;Get GETDP0INC
        move dpc, #0 ;Set DP0 to byte mode
        move dp[0], #string_table + 8000h

        str_search_loop:
        call a[1] ;Get a string length
        djnz lc[0], next_str ;If not this string, go to next
        move lc[0], gr ;Otherwise, put len in LC0
        move acc, @dp[0]++ ;...and point past length

        out_loop:
        call a[2] ;Get a char and bump pointer
        call char_out ;Output the character
        djnz lc[0], out_loop ;If more characters, loop
        ret ;Otherwise, were done.

        next_str:
        move acc, gr ;GR contains len of this string
        add dp[0] ;Add current ptr to current len...
        move dp[0], acc ;...to create a new pointer
        jump str_search_loop ;Jump back and test index again

        ;
        ; Each entry in the string table begins with the string length
        ; followed by the string characters.
        ;
        string_table:
        dc8 string1 - string_table
        dc8 "This is the first string."
        string1:
        dc8 string2 - string1
        dc8 "This is a second example of a string"
        string2:
        dc8 string3 - string2
        dc8 "A third string."
        string3:
        dc8 string4 - string3
        dc8 "Finally, a fourth string in the array!!!"
        string4:




        關鍵詞:

        評論


        相關推薦

        技術專區

        關閉
        主站蜘蛛池模板: 西乌| 阳谷县| 枣强县| 灵璧县| 利津县| 家居| 岑溪市| 湘潭市| 南郑县| 夏邑县| 台州市| 邓州市| 雷山县| 海原县| 灵台县| 翼城县| 顺义区| 平山县| 昔阳县| 城市| 两当县| 灌阳县| 大连市| 巩留县| 岐山县| 鹰潭市| 威信县| 赣榆县| 个旧市| 无锡市| 刚察县| 杭锦旗| 台前县| 宜章县| 金堂县| 全州县| 华容县| 云安县| 富民县| 德昌县| 黄陵县|