2010-06-15 823 views
7

我試圖從Cortex-M3處理器(STM32)上的RAM執行一個函數。該功能擦除和重寫內部閃存,所以我肯定需要在RAM中,但我該怎麼做?如何從Cortex-M3(STM32)上的RAM執行函數?

我試過的是:使用memcpy將函數複製到RAM中的一個字節數組(檢查它是否正確對齊),設置函數指針指向字節數組,然後調用函數(指針) 。

這工作正常,也許10條指令(我可以跟隨執行與調試器),但然後我得到一個總線錯誤,處理器重置。 buss錯誤發生在第二遍循環中,所以代碼應該沒問題(因爲它工作在第一遍)。我在想,更快的RAM訪問會以某種方式堵塞總線時序...

無論如何,是否有正確的方法來做到這一點?分散文件如何看起來像是將函數自動放入RAM中(我正在使用Keil uVision for Cortex-M3)?

編輯:更多信息: 工具鏈:的RealView MDK-ARM V 4.10 編譯器:ARMCC v4.0.0.728 彙編程序:ARMASM v4.0.0.728 接頭:armlink將v4.0.0.728 處理器:STM32F103ZE

當復位發生時,IMPRECISERR位在總線錯誤寄存器中置1。

+0

嗯,我認爲你應該多或交小於製造商的論壇,您使用的是特殊的硬件,但是,你能不能提供更多的細節,你使用的是什麼編譯器,你怎麼調試它,任何代碼樣本,也許*一個*有知識的人可以回答這個問題,除此之外...... – t0mm13b 2010-06-15 09:39:20

+1

@ tommieb75:但我更喜歡你們! – c0m4 2010-06-15 09:42:36

回答

7

循環迭代時的崩潰可能是因爲該函數分支到一個絕對地址並且不相對於RAM中的新函數位置。由於閃存擦除操作,在該點訪問原始代碼位置是否會導致總線錯誤?

我相信你可以通過在函數定義中附加__ram指令來標記一個函數,以便用CARM正確地編譯和複製到RAM中。有關如何做同樣的用的RealView編譯器指令看到EXECUTING FUNCTIONS IN RAM技術支持文章:

的μVision讓你 定位模塊是 在對話框項目進入特定的存儲區 - 選項 - 目標。爲此,請右鍵點擊源文件(或文件組) 並打開對話框選項 - 屬性。然後選擇內存 區域根據內存分配

在文件夾ARMExamplesRAM_Function中有一個示例。

這應該生成啓動代碼,負責將功能複製到RAM並正確地將呼叫鏈接到該位置。否則,如果您需要動態地將任意函數複製到RAM,請查看使用RealView編譯position independent code (PIC)

+0

優秀的答案! – c0m4 2010-06-15 13:54:18

+0

我有同樣的問題,除了我沒有使用RTX或任何庫,所以我不包括編譯器的代碼,自動將RAM函數加載到RAM中。 我想有一個加載區或執行區的RAM在我的代碼實際上將被鏈接到,但我需要的JTAG程序員的代碼加載到另一個地址(閃光燈)。 理想情況下,在.sct文件中(我正在使用Keil MDK)我會有一段指定存儲和鏈接地址的部分。 但我還沒有制定出如何做到這一點呢。 – 2010-08-10 03:54:38

+0

@Captain:你可能想考慮用你的具體情況開一個新的問題。不過,我相信上述解決方案也適用於您的情況。 – 2010-08-10 13:04:50

2

不知道更多關於你的情況我只能提出一些一般的東西......確保你有一個有效的堆棧(或避免函數中的所有堆棧操作),你的中斷被禁用,系統向量表中的任何向量都不會指向擦除閃存時消失的代碼。最後,確保你的功能是鏈接運行在你放置它的地址...代碼可能不可重定位,並可能跳轉到舊地點的位置。

1

IAR編譯器(我知道你的問題是關於Keil的,但我不把它一起玩),你可以標記,或者整個項目或單個文件是「位置無關」。從過去將其與其他處理器一起使用,這意味着您可以將它「隨處」移動,並且仍然可以正常工作

2

由於ARM加載立即數據的能力有限,因此爲ARM生成代碼的實用程序經常並置代碼和數據。例如,像

void myRoutine(void) 
{ 
    myVar1=0x12345678; 
    myVar2=0x87654321; 
} 

聲明可能最終成爲類似:

myRoutine:   
    ldr r0,=myVar1; Load the address of _myVar 
    ldr r1,=0x12345678 
    str r1,[r0] 
    ldr r0,=myVar1; Load the address of _myVar 
    ldr r1,=0x87654321 
    str r1,[r0] 
    bx lr 

which would get translated into: 
    ldr r0,dat1 
    ldr r1,dat2 
    str r1,[r0] 
    ldr r0,dat3 
    ldr r1,dat4 
    str r1,[r0] 
    bx lr 
... followed some time later by 
dat1 dcd _myVar1 
dat2 dcd 0x12345678 
dat3 dcd _myVar2 
dat4 dcd 0x12345678 

or perhaps even something like: 
    mar r0,dat1 
    ldrm r0,[r1,r2,r3,r4] 
    str r2,[r1] 
    str r4,[r3] 
    bx lr 
... followed some time later by 
dat1 dcd _myVar1 
dat2 dcd 0x12345678 
dat3 dcd _myVar2 
dat4 dcd 0x12345678 

注意_myVar和0x12345678的可以立即放置在它們出現的程序代碼如下;如果您嘗試使用最後一條指令後面的標籤來確定例程的長度,則此長度將無法包含補充數據。

與ARM需要注意的另外一點是,由於歷史的原因,代碼的地址會經常有,雖然代碼實際上開始於半字的邊界他們最顯著位爲偶數。因此,地址爲0x12345679的指令將佔用從0x12345678開始的兩個或四個字節。這可能會使memcpy之類的地址計算複雜化。

我的建議是用匯編語言編寫一個小的程序,做你所需要的。這只是一些說明,您可以確切知道代碼正在做什麼以及它可能具有哪些地址依賴關係,並且您不必擔心未來的編譯器版本會以某種方式更改您的代碼,從而導致破壞某些內容[例如,上面的代碼的第三個版本也沒問題,即使dat1降落奇數半字邊界由於M3的LDR指令可以處理未對齊的讀取,但第四(略更快和更緊湊),使用LDRM在這樣的情況下會失敗版本;即使當今版本的編譯器使用四條LDR指令,未來的版本也可能使用LDRM]。