在本系列關(guān)于將 RTOS 與 MPU 結(jié)合使用的最后一部分中,我們將了解如何按進(jìn)程對(duì) RAM 進(jìn)行分組,并以使用 Cortex-M MPU 時(shí)的建議列表作為結(jié)尾。
到目前為止,我們已經(jīng)了解了 MPU 是什么以及它如何幫助將任務(wù)和流程相互隔離。我們還研究了如何設(shè)置 Cortex-M MPU,發(fā)現(xiàn)它非常易于使用。使用 MPU 的復(fù)雜性更多地與組織應(yīng)用程序的內(nèi)存有關(guān),而不是更新這個(gè)非常有用的設(shè)備的機(jī)制。
在本系列關(guān)于將 RTOS 與 MPU 結(jié)合使用的最后一部分中,我們將了解如何按進(jìn)程對(duì) RAM 進(jìn)行分組,并以使用 Cortex-M MPU 時(shí)的建議列表作為結(jié)尾。
創(chuàng)建 MPU 進(jìn)程表
使用 MPU 時(shí)最大的困難可能是按進(jìn)程對(duì)內(nèi)存進(jìn)行分組并創(chuàng)建 MPU 進(jìn)程表。這部分是因?yàn)槟枰钊氲亓私饽墓ぞ哝湥壕幾g器、匯編器和鏈接器/定位器。
假設(shè)我使用的是 IAR 工具鏈(即 EWARM),但概念非常相似,您可以根據(jù)自己使用的工具調(diào)整這些概念。除非另有說(shuō)明,否則鏈接器會(huì)將數(shù)據(jù)(即 RAM)放置在圖 2 所示的三個(gè)部分之一中。
未初始化的數(shù)據(jù)
零初始化數(shù)據(jù)
初始化數(shù)據(jù)
顧名思義,未初始化的數(shù)據(jù)對(duì)應(yīng)于在編譯時(shí)未賦予初始值或未聲明為靜態(tài)的變量。
零初始化數(shù)據(jù)對(duì)應(yīng)于聲明為靜態(tài)并在啟動(dòng)時(shí)初始化為零的數(shù)據(jù)。鏈接器將其分組為一個(gè)連續(xù)的塊,以便啟動(dòng)代碼可以執(zhí)行塊集(為 0)。
初始化數(shù)據(jù)對(duì)應(yīng)于具有初始值的數(shù)據(jù)(例如 int x = 10;)。同樣,鏈接器將這些數(shù)據(jù)分組到一個(gè)連續(xù)的塊中,但在 ROM 中創(chuàng)建一個(gè)并行塊,其中包含 RAM 中每個(gè)相應(yīng)變量的初始值。啟動(dòng)時(shí),整個(gè)塊從 ROM 復(fù)制到 RAM。

【圖2 | RAM 部分。]
如前所述,進(jìn)程的 RAM 必須連續(xù)分組,如圖 3 所示。為此,我們需要繞過(guò)編譯器/鏈接器標(biāo)準(zhǔn)部分并創(chuàng)建將按進(jìn)程分組的新部分。工具鏈通常能夠創(chuàng)建多個(gè)零塊和初始化部分,如圖 3 所示。

【圖3 | 基于 MPU 的應(yīng)用程序的 RAM 部分。]
創(chuàng)建命名的 RAM 部分
要按進(jìn)程對(duì)數(shù)據(jù)進(jìn)行分組,我們需要使用 EWARM #pragma 指令 default_variable_attributes 并將所有要在一個(gè)進(jìn)程中分組的變量包裝在一起。
#pragma default_variable_attributes = @”.Process1”
// All variables that we want to be part of the section named “.Process1”。
#pragma default_variable_attributes =
如果您的應(yīng)用程序包含在匯編語(yǔ)言文件中聲明的變量,那么您還需要確保匯編語(yǔ)言文件包含適當(dāng)?shù)膮R編程序指令。
按塊對(duì) RAM 進(jìn)行分組
您的應(yīng)用程序肯定會(huì)包含不一定與任何特定進(jìn)程相關(guān)聯(lián)的代碼。在這種情況下,最好為這些模塊創(chuàng)建命名部分,然后將這些部分組合成一個(gè)公共代碼塊。然后,您將使用上述#pragma 指令創(chuàng)建不同的命名段,每個(gè)模塊一個(gè),并使用鏈接器的 塊 指令(如下所示)對(duì)這些段進(jìn)行分組。
define block COMMON_RAM_BLOCK with alignment = 4K, size = 4K
{
section .DRIVER_RAM,
section .COMMOM_RAM,
section .MATH_RAM,
section .STRING_RAM,
}
define block PROCESS_AI_RAM_BLOCK with alignment = 16K, size = 16K
{
section .AI_DRIVER_RAM, // Analog input driver
section .RTD_LIN_RAM, // RTD linearization
section .THERMOCOUPLE_LIN_RAM, // Thermocouple linearization
section .UNIT_CONVERSION_RAM, // Shared RAM with AO module
}
define block PROCESS_AO_RAM_BLOCK with alignment = 8K, size = 8K
{
section .AO_DRIVER_RAM, // Analog output driver
section .4_20MA_LIN_RAM, // 4-20 mA linearization
section .ACTUATOR_LIN_RAM, // Actuator linearization
section .UNIT_CONVERSION_RAM, // Shared RAM with AI module
}
define block SHARED_RAM_BLOCK with alignment = 2K, size = 2K
{
}
您會(huì)注意到 block 指令允許您指定內(nèi)存塊的大小和對(duì)齊方式。為了將塊的起始地址放置在 MPU 進(jìn)程表中,兩個(gè)值必須相同,這一點(diǎn)很重要。此外,每個(gè)塊所需的 RAM 量取決于應(yīng)用程序。為了便于說(shuō)明,我決定使用 16K、8K、4K 和 2K 字節(jié)。
定位 RAM 塊
我們現(xiàn)在可以使用兩個(gè)鏈接器指令將所有塊放置在 MCU 的可尋址空間中:區(qū)域和位置:
define region RAM = Mem:[from 0x20000000 size 64K];
place in RAM
{
block RAM_ALL with fixed order
{
block PROCESS_AI_RAM_BLOCK,
block PROCESS_AO_RAM_BLOCK,
block COMMON_RAM_BLOCK,
block SHARED_RAM_BLOCK
}
}
region 指令指定 MCU 的可尋址存儲(chǔ)器。如果您的 RAM 并非全部連續(xù),則可能有不同的區(qū)域指令。
RAM 指令中的位置指定在 RAM 區(qū)域中定位塊。您會(huì)注意到我們需要將塊放入塊中以指定塊放置的順序。事實(shí)上,為了減少浪費(fèi)的空間量,應(yīng)該先使用較大的塊。
為每個(gè)任務(wù)創(chuàng)建 MPU 進(jìn)程表
現(xiàn)在 RAM 按進(jìn)程分組,您可以返回并編輯每個(gè)任務(wù)/進(jìn)程的 MPU 表。但是,要做到這一點(diǎn),編譯器必須知道塊的名稱,因此,您需要使用 #pragma section 指令,如下所示:
#pragma section = “COMMON_RAM_BLOCK”
#pragma section = “PROCESS_AI_RAM_BLOCK”
#pragma section = “PROCESS_AO_RAM_BLOCK”
#pragma section = “SHARED_RAM_BLOCK”
這兩個(gè)進(jìn)程表現(xiàn)在可以如下所示(假設(shè)您沒(méi)有使用包含上一節(jié)中描述的每個(gè)任務(wù)回調(diào)的版本):


建議
以下是使用 Armv7-M MPU 時(shí)的一些建議。
在非特權(quán)模式下運(yùn)行用戶代碼:
可以使用 MPU,但仍以特權(quán)模式運(yùn)行所有應(yīng)用程序代碼。當(dāng)然,這意味著應(yīng)用程序代碼將能夠更改 MPU 設(shè)置,因此會(huì)破壞擁有 MPU 的目的之一。最初以特權(quán)模式運(yùn)行應(yīng)用程序可能會(huì)更容易遷移應(yīng)用程序代碼。但是,在某些時(shí)候,您的大部分應(yīng)用程序代碼都需要在非特權(quán)模式下運(yùn)行,因此您需要添加 SVC 處理程序。
將 PRIVDEFENA 設(shè)置為 1:
這允許特權(quán)代碼訪問(wèn)完整的內(nèi)存映射。理想情況下,您的大多數(shù)應(yīng)用程序?qū)⒃诜翘貦?quán)模式下運(yùn)行,只有 ISR 和 RTOS 將在特權(quán)模式下運(yùn)行。此建議可避免為每個(gè)任務(wù)使用三個(gè) MPU 區(qū)域,以授予特權(quán)代碼訪問(wèn)任何 RAM 位置、任何代碼和任何外圍設(shè)備。將 PRIVDEFENA 設(shè)置為 1 的決定可能已經(jīng)由 RTOS 供應(yīng)商做出,您無(wú)法更改。
ISR 具有完全訪問(wèn)權(quán)限:
每當(dāng)識(shí)別到中斷并啟動(dòng) ISR 時(shí),處理器就會(huì)切換到特權(quán)模式。由于 PRIVDEFENA 將設(shè)置為 1,因此 ISR 無(wú)論如何都可以訪問(wèn) I/O 位置的任何內(nèi)存。您根本不想在進(jìn)入 ISR 時(shí)重新配置 MPU,并在退出時(shí)重新配置它。因此,ISR 應(yīng)該被視為系統(tǒng)級(jí)代碼,因此確實(shí)應(yīng)該被允許具有完全訪問(wèn)權(quán)限。
此外,ISR 應(yīng)始終盡可能短,并簡(jiǎn)單地向任務(wù)發(fā)出信號(hào)以執(zhí)行中斷設(shè)備所需的大部分工作。當(dāng)然,這假設(shè) ISR 是內(nèi)核感知的,并且任務(wù)有相當(dāng)多的工作來(lái)處理中斷設(shè)備。例如,處理以太網(wǎng)數(shù)據(jù)包不應(yīng)該在 ISR 級(jí)別完成。但是,可以直接在 ISR 中切換 LED 或更新脈沖寬度調(diào)制 (PWM) 定時(shí)器的占空比。
將 XN 位設(shè)置為 1:
如果您的應(yīng)用程序代碼不希望在 RAM 外執(zhí)行代碼,則應(yīng)為所有 RAM 或外圍區(qū)域設(shè)置 RASR 寄存器的 eXecute Never 位。為外圍設(shè)備設(shè)置 XN 位可能看起來(lái)很奇怪,但它不會(huì)傷害并防止黑客試圖進(jìn)入您的系統(tǒng)。
限制外圍設(shè)備對(duì)其進(jìn)程的訪問(wèn):
您應(yīng)該留出一個(gè)或多個(gè) MPU 區(qū)域來(lái)限制進(jìn)程只能訪問(wèn)其自己的外圍設(shè)備。換句話說(shuō),如果一個(gè)進(jìn)程管理 USB 端口,那么它應(yīng)該只能訪問(wèn) USB 外圍設(shè)備或與 USB 控制器需求相關(guān)的外圍設(shè)備,例如 DMA。
限制 RTOS API:
系統(tǒng)設(shè)計(jì)人員需要確定哪些 RTOS API 應(yīng)可用于應(yīng)用程序代碼。具體來(lái)說(shuō),您想防止應(yīng)用程序代碼在系統(tǒng)初始化后創(chuàng)建和刪除任務(wù)或其他 RTOS 對(duì)象(如信號(hào)量、隊(duì)列等)嗎?換句話說(shuō),RTOS 對(duì)象是否應(yīng)該只在系統(tǒng)啟動(dòng)時(shí)創(chuàng)建,而不是在運(yùn)行時(shí)創(chuàng)建?如果是這樣,那么 SVC 處理程序查找表應(yīng)該只包含您想要向應(yīng)用程序公開(kāi)的 API。然而,即使 ISR 在特權(quán)模式下運(yùn)行并因此可以訪問(wèn)任何 RTOS API,一個(gè)好的 RTOS 仍會(huì)阻止從 ISR 創(chuàng)建和刪除 RTOS 對(duì)象。
在 RTOS 空間中分配 RTOS 對(duì)象:
任務(wù)堆棧位于進(jìn)程的內(nèi)存空間內(nèi)。然而,RTOS 對(duì)象(信號(hào)量、隊(duì)列、任務(wù)控制塊等)最好分配在內(nèi)核空間中并通過(guò)引用進(jìn)行訪問(wèn)。換句話說(shuō),您不想在進(jìn)程的內(nèi)存空間中分配 RTOS 對(duì)象,因?yàn)檫@意味著應(yīng)用程序代碼可以有意或無(wú)意地修改這些對(duì)象,而無(wú)需通過(guò) RTOS API。
沒(méi)有全局堆:
將 MPU 設(shè)置為使用全局堆(即所有進(jìn)程使用的堆)幾乎是不可能的,因此您應(yīng)該盡可能避免使用這些堆。相反,如前所述,如果進(jìn)程需要?jiǎng)討B(tài)分配的內(nèi)存(例如以太網(wǎng)幀緩沖區(qū)),則應(yīng)允許進(jìn)程特定的堆。
不要禁用中斷:
如果您的應(yīng)用程序在非特權(quán)模式下運(yùn)行,任何禁用中斷的嘗試都將被忽略。這樣做的問(wèn)題是,您不會(huì)從 CPU 獲得中斷 未被 禁用的指示。
如果您的應(yīng)用程序在非特權(quán)模式下運(yùn)行并且您嘗試通過(guò) NVIC 禁用中斷,則會(huì)觸發(fā)總線故障。
保護(hù)對(duì)代碼的訪問(wèn):
盡管 MPU 區(qū)域通常用于提供或限制對(duì) RAM 和外圍設(shè)備的訪問(wèn),但如果您有空閑區(qū)域并且能夠按進(jìn)程組織代碼(通過(guò)鏈接器命令),那么限制代碼對(duì)代碼的訪問(wèn)可能很有用。這可以防止某些類型的安全攻擊,例如 Return-to-libc [2]。
減少進(jìn)程間通信:
就像任務(wù)應(yīng)該設(shè)計(jì)得盡可能獨(dú)立一樣,流程也應(yīng)該遵循同樣的規(guī)則。因此,要么進(jìn)程不相互通信,要么將進(jìn)程間通信保持在最低限度。
如果您必須與其他進(jìn)程通信,只需留出一個(gè)包含輸出和輸入緩沖區(qū)的共享區(qū)域。發(fā)送方將其數(shù)據(jù)放入輸出緩沖區(qū),然后觸發(fā)中斷以喚醒接收進(jìn)程。一旦數(shù)據(jù)被處理,響應(yīng)(如果需要)可以放在發(fā)送者的緩沖區(qū)中,并且可以使用中斷來(lái)通知發(fā)送者。
確定遇到 MPU 故障時(shí)該怎么做:
理想情況下,在開(kāi)發(fā)過(guò)程中檢測(cè)并糾正所有 MPU 故障。您應(yīng)該計(jì)劃由于意外故障或錯(cuò)誤或您的系統(tǒng)受到安全攻擊而在現(xiàn)場(chǎng)發(fā)生故障。在大多數(shù)情況下,建議對(duì)每個(gè)任務(wù)或每個(gè)進(jìn)程都有一個(gè)受控的關(guān)閉順序。是否重新啟動(dòng)有問(wèn)題的任務(wù)、進(jìn)程內(nèi)的所有任務(wù)或整個(gè)系統(tǒng)取決于故障的嚴(yán)重程度。
有辦法記錄和報(bào)告故障:
理想情況下,您有辦法記錄(可能記錄到文件系統(tǒng))并顯示故障原因,以允許開(kāi)發(fā)人員解決問(wèn)題。
結(jié)論
內(nèi)存保護(hù)單元 (MPU) 是將對(duì)內(nèi)存和外圍設(shè)備的訪問(wèn)限制為僅需要訪問(wèn)這些資源的代碼的硬件。如果任務(wù)試圖訪問(wèn)其分配空間之外的內(nèi)存位置或外圍設(shè)備,則會(huì)觸發(fā) CPU 異常,并且根據(jù)應(yīng)用程序,必須采取糾正措施。
Cortex-M MCU 中的 MPU 是一個(gè)相當(dāng)簡(jiǎn)單的設(shè)備,并且相對(duì)容易配置。然而,使用 MPU 的復(fù)雜性更傾向于按進(jìn)程分配存儲(chǔ)(主要是 RAM)以及創(chuàng)建將在上下文切換期間加載到 MPU 中的 MPU 進(jìn)程表。
最后,我提供了一個(gè)建議列表,這些建議可以更好地在您的應(yīng)用程序中使用 MPU。
單獨(dú)的軟件無(wú)法阻止對(duì)未分配給 RTOS 環(huán)境中任務(wù)的內(nèi)存或外圍設(shè)備的訪問(wèn)。您需要硬件來(lái)實(shí)現(xiàn)這一點(diǎn),而 MPU 是目前 Cortex-M (Armv7-M) 上唯一可以做到這一點(diǎn)的機(jī)制。
遷移應(yīng)用程序以使用 MPU 是一個(gè)相當(dāng)簡(jiǎn)單但乏味的過(guò)程。添加 MPU 也會(huì)給您的應(yīng)用程序帶來(lái)開(kāi)銷:在上下文切換期間您需要加載額外的寄存器,并且用戶代碼應(yīng)該在非特權(quán)模式下運(yùn)行以避免此類代碼更改 MPU 設(shè)置。
審核編輯:郭婷
-
mcu
+關(guān)注
關(guān)注
147文章
19127瀏覽量
403852 -
寄存器
+關(guān)注
關(guān)注
31文章
5619瀏覽量
130419 -
MPU
+關(guān)注
關(guān)注
0文章
463瀏覽量
51571
發(fā)布評(píng)論請(qǐng)先 登錄
如何對(duì) iMX8M Plus MPU 進(jìn)行“熱復(fù)位”或“熱啟動(dòng)”?
Renesas RZ/T2M:高性能MPU的技術(shù)剖析與應(yīng)用指南
RZ/G2L與RZ/G2LC系列MPU深度解析:硬件設(shè)計(jì)開(kāi)發(fā)的新選擇
Cortex-M0和Cortex-M0+的區(qū)別
最小化ARM Cortex-M CPU功耗的方法與技巧分享
Cortex-M0 處理器介紹
進(jìn)程概念和特征
RZ/T2M MPU:工業(yè)控制與自動(dòng)化應(yīng)用的理想之選
Cortex-M產(chǎn)品的特色
Cortex-M內(nèi)核中的精確延時(shí)的方法
Cortex-M級(jí)別的轉(zhuǎn)換
請(qǐng)問(wèn)NuMicro? Cortex-M? 系列芯片是否支持 I2C 監(jiān)視器功能?
如何使用 SPI 全雙工在兩個(gè) 5LP MPU 之間連接 RAM?
瑞薩RZ/A3M HMI MPU介紹
如何按進(jìn)程對(duì)RAM進(jìn)行分組與Cortex-M MPU使用建議
評(píng)論