--- title: FreeRTOS (MMU) categories: embedded, arm, rtos, beaglebone black, am335x, mmu toc: no ... 協作者 === * 2015 年春季 - `陳冠任`_, `許士杰`_, `黃睦林`_, `孫瑋`_, `涂逸祥`_ 共筆 == * 2015 年春季 - `hackpad`_ **目錄** ====== * `Beaglebone black 簡介<#beaglebone-black-簡介>`_ - `硬體簡介<#硬體簡介>`_ - `開機流程<#開機流程>`_ * `FreeRTOS on Beaglebone Black<#freertos-on-beaglebone-black>`_ - `開發環境<#開發環境>`_ - `實作過程<#實作過程>`_ - `移植FreeRTOS 8.2.1到BBB<#移植freertos-8.2.1到bbb>`_ * `ARMv7-A MMU Architecture<#armv7-a-mmu-architecture>`_ - `Sections and pages<#sections-and-pages>`_ - `Translation Lookaside Buffers (TLB)<#translation-lookaside-buffers-tlb>`_ - `保護機制與記憶體行為<#保護機制與記憶體行為>`_ * `ARMv7-A 啟用MMU設定<#armv7-a-啟用mmu設定>`_ - `MRC instruction<#mrc-instruction>`_ - `MMU啟用流程<#mmu啟用流程>`_ * `問題討論<#問題討論>`_ `Beaglebone black 簡介<#目錄>`_ =========================== `硬體簡介<#目錄>`_ ------------ * Processor: `AM335x 1GHz ARM® Cortex-A8`_ - 512MB DDR3 RAM - 4GB 8-bit eMMC on-board flash storage - 3D graphics accelerator - NEON floating-point accelerator - 2x PRU 32-bit microcontrollers * Connectivity - USB client for power & communications - USB host - Ethernet - HDMI - 2x 46 pin headers * Software Compatibility - Debian - Android - Ubuntu - Cloud9 IDE on Node.js w/ BoneScript library - plus much more .. image:: http://www.circuidipity.com/images/bbb-details3.png `開機流程<#目錄>`_ ------------   當Beaglebone Black(BBB)一上電後,即開始執行ROM裡面的ROM code。再來根據腳位SYSBOOT的值來決定讀取哪裡的MLO檔,預設是讀取板子上eMMC的MLO檔。再來靠MLO讀取u-boot,u-boot再根據文件uEnv.txt載入image或file system。 .. image:: https://c1.staticflickr.com/9/8827/17633638240_e7c3e007a4_o.jpg   若是按住BBB板子上的S2按鍵,根據下圖,會讓SYS_BOOT2變成低電位,使得BBB的ROM code載入MMCSD內的MLO並執行。之後再根據MLO的程式碼來決定要載入什麼檔案執行。在我們第一次實驗時,是利用MLO去載入app並執行。 .. image:: https://c2.staticflickr.com/6/5462/17823806425_3996a193f6_o.png `FreeRTOS on Beaglebone Black<#目錄>`_ ==================================== `開發環境<#目錄>`_ ------------ * 主機環境:Ubuntu 14.10、Linux Mint 17 * 開發板:Beaglebone black (Rev C) * Cross compiler: https://launchpad.net/gcc-arm-embedded/+download 下載並解壓至喜歡的地方 `實作過程<#目錄>`_ ------------ **下載程式碼:** .. code-block:: bash git clone https://github.com/henfos/BBBFreeRTOS.git cd BBBFreeRTOS/Demo/AM3359_BeagleBone_GCC    **修改 main.c ,在開頭補上 :** .. code-block:: c #include    **修改 makefile:** 將以下原本的參數 .. code-block:: makefile CC=/home/henrifo/Nedlastinger/gcc-arm-none-eabi-4_8-2013q4/bin/arm-none-eabi-gcc OBJCOPY=/home/henrifo/Nedlastinger/gcc-arm-none-eabi-4_8-2013q4/bin/arm-none-eabi-objcopy ARCH=/home/henrifo/Nedlastinger/gcc-arm-none-eabi-4_8-2013q4/bin/arm-none-eabi-ar 改成 .. code-block:: makefile CC={Cross compiler解壓的路徑}/arm-none-eabi-gcc OBJCOPY={Cross compiler解壓的路徑}/arm-none-eabi-objcopy ARCH={Cross compiler解壓的路徑}/arm-none-eabi-ar    **接下來執行make,會產生 rtosdemo-a.bin** **編寫uEnv.txt讓u-boot根據其而載入rtosdemo-a.bin,內容如下** .. code-block:: txt bootcmd=fatload mmc 0 0x80500000 rtosdemo-a.bin; go 0x80500000; uenvcmd=boot    **將MLO、uEnv.txt、u-boot.img、rtosdemo-a.bin放入MicroSD卡** **按住BBB的S2鈕並開機** 結果圖如下,可看出FreeRTOS已能初步運作。 .. image:: https://c1.staticflickr.com/1/476/18159470350_95ea35c12d_o.png    **將main.c的3個task,刪減到剩下1個。 將delay的計數器從0x1FFF改成0x3FF0000,增加閃爍時間間隔。 並將serial_puts的function中加入'\\r',將輸出的游標推到每行起始位置。** 結果圖如下,輸出的結果比較清晰且LED的閃爍可以用肉眼察覺。 .. image:: https://c1.staticflickr.com/9/8781/18344147282_2ebf303d4d_o.png `移植FreeRTOS 8.2.1到BBB<#目錄>`_ ---------------------------- **到FreeRTOS官網下載`8.2.1版 (目前最新版本)`_的source** **解壓後將8.2.1版本的source資料夾替換掉原本的source後,再來將原本的Source/portable/GCC/AM335_BeagleBone複製回去。** **然後接著修改Source/include資料夾底下的portable.h檔** 將 .. code-block:: header file #ifdef GCC_AM335_BeagleBoard #include "../../Source/portable/GCC/AM335_BeagleBone/portmacro.h" #endif 加至 .. code-block:: header file #ifndef portENTER_CRITICAL #include "portmacro.h" #endif 的前面,以免發生錯誤。    **接著修改Source/portable/GCC/AM335_BeagleBone底下的portmacro.h** 在裡面加入 .. code-block:: header file typedef portSTACK_TYPE StackType_t; typedef long BaseType_t; typedef unsigned long UBaseType_t; typedef uint32_t TickType_t;    **並將下面的 portTICK_RATE_MS 改為 portTICK_PERIOD_MS** **修改Source/portable/GCC/AM335_BeagleBone/portISR.c檔案裡的vTaskIncrementTick改為xTaskIncrementTick (165行)** 出現結果與上圖相同,但仍有許多warning需解決。 `ARMv7-A MMU Architecture<#目錄>`_ ================================ `Sections and pages<#目錄>`_ -------------------------- ARM 的 MMU 支援 四種 page sizes(比較大的稱 section,比較小的稱 pages ) * Supersections: 16 MB memory blocks (24-bit offsets) * Sections: 1 MB memory blocks (20-bit offsets) * Large pages: 64 KB pages (16-bit offsets) * Small pages: 4 KB pages (12-bit offsets)   The MMU supports a two-level hierarchy for its page table structure. .. image:: https://c1.staticflickr.com/1/501/18421058670_79ed99103a_o.png first-level table 功能: 1. 包含pointer,指向 second-level tables 2. 當做 section 或 supersection 的 base address `Translation Lookaside Buffers (TLB)<#目錄>`_ ------------------------------------------- TLB分為以下兩種: 1. Micro TLB (The first-level TLB) 2. Main TLB (The second-level TLB)   Micro TLB 特色介紹: * 最小也最快 * 分成兩部分,for instruction 和 for data * can store 32 entries * fully associative and can perform a lookup in one clock cycle * 使用 address space identifier(ASID) * Allow the operating system to identify one process’ address space from another’s without flushing the cache. * 避免當Task Context-Switch發生時,TLB被Flush的成本 * Entries can also be tagged as global --> shared * 每一個 entry 都有 protection bits, 用來確認每一個 address lookup * 如果 protections 不允許記憶體存取,則 MMU會發出 Data Abort 的訊號,將會造成一個 trap * 發生cache miss時,replacement algorithm 可能採用 round-robin (the default) 或 random replacement 的 policy   Main TLB 介紹: * 使用時機: cache misses from the microTLBs * 他只有一個,意味著 Main TLB 要處理 data-side 或是 instruction-side MicroTLBs 產生的 misses * eight-way set associative with 64-byte blocks and 1 MB capacity * 每個Main TLB項目都會包括 - Virtual Address - Page Size - Physical Address - Memory Properties   .. image:: https://c1.staticflickr.com/9/8887/17986805824_0c95da481b_o.png Figure 2 表示出所有 elements 運作流程: 1. 第一步是在 MicroTLB . Instruction fetch 時存取 Instruction MicroTLB 以及在 data read/write operation時去存取Data MicroTLB。 一個資料被存取除了查表要查的到以外,還需要access permission,如果沒有 proper permissions,會產生 trap (Data Abort signal)。如果發生 miss,就會到step 2 ( 去 lookup Main TLB )。 2. 當 MicroTLB lookup 產生 cache miss,會往 Main TLB去找. Lookup過程與 MicroTLB相同(matched requested page and permissions)。如果 miss,到 step 3. 3. 最後的 step 是 page table(s) (在此稱為 translation table work)。系統支援 2個 first-level tables。VA 的 high-order bits 決定使用哪一個 table。根據VA的 topmost n bits是0(用 TTBR0)或者不是0(用TTBR1)。而 n 的值取決於Translation Table Base Control Register (TTBCR)。operating system 和 memory-mapped I/O 位於 upper part of the address space 並被 TTBR1 管理。user processes 在 lower part of memory 並被 TTBR0 管理。當在context switch時,operating system 為了新的 process, 必須切換到TTBR0去指向first-level table 。TTBR0仍將保留operating system 和 memory-mapped I/O 的 memory map。 經由記憶體中page table來查找位址被稱為translation table walk,因為它可能涉及經過不同階層的table。藉由ARM MMU,section可由first-level page table直接對應出位址;page則是兩步驟的過程。如果是section,則實體基準位址是被儲存在first-level table的page table entry中,若是page,則是second-level table的位址儲存在first-level table的page table entry中。   以ARMv7架構為例,要控制TLB Translation Table,可以透過CP15的暫存器c2,其中c2主要提供以下Translation table base registers .. image:: https://c1.staticflickr.com/1/436/17986805674_4d0e1757f6_o.png   下面來談談根據TLB的設定參數組合,分別以基於16MB(SuperSection),1MB(Section),64KB(Large Page),4KB(Small Page)不同分頁的組合,來說明TLB 1級與2級 Table的運作概念, 如下所示為16MB(SuperSection) 配置下,TLB分頁運作的概念 .. image:: https://c1.staticflickr.com/1/466/18611607711_c9d17b382c_o.png   如下所示為1MB(Section) 配置下,TLB分頁運作的概念 .. image:: https://c1.staticflickr.com/9/8875/18583078246_3d9e150433_o.png   如下所示為64KB(Large Page) 配置下,TLB分頁運作的概念 .. image:: https://c1.staticflickr.com/1/271/18609453405_83fd62aed2_o.png   如下所示為4KB(Small Page) 配置下,TLB分頁運作的概念 .. image:: https://c1.staticflickr.com/9/8899/18609452125_8c1c539f48_o.png `保護機制與記憶體行為<#目錄>`_ ------------------ 每次記憶體存取都會跟每個記憶體區塊的page table entry中儲存的權限做確認,無論是page還是section都是。而且page table entry還可以指定一個記憶體區域中對於其他核心或處理器來說的記憶體被修改的可見性。記憶體區域可以有以下特性: * Execute never: 阻止處理器中的指令去存取這個區域的記憶體 * Read-only, read/write, no access: 這幾個模式可在user-mode和privileged (kernel) mode中做不同設定。例如,kernel 的記憶體可被標示為user mode不能存取,但kernel mode可以讀寫。 * non-secure: 標示記憶體區域為"信任的" * sharable: 這個可以標示是否一個區域的記憶體可以與其他處理器共用,或是可以映射到硬體裝置。有以下幾種模式可以被設定: * Strongly ordered: 記憶體存取必須根據程式執行的順序 * Device/shared or device/non-shared: 這記憶體是直接對應到硬體裝置(因此沒有快取),且這個裝置是否與其他處理器在匯流排中共享。 * normal/shared, normal/non-shared: 一般的記憶體使用,再來看可否在與其他處理器在匯流排上共享。 若是記憶體存取沒有通過權限,MMU會發出Memory Abort訊號給處理器。 `ARMv7-A 啟用MMU設定<#目錄>`_ ======================= MMU 可以透過 system control coprocessor (CP15) registers 進行設定 (`reference here`_) `CP15`_ 又稱系統控制協同處理器 (system control coprocessor),其中有 c0 到 c15 暫存器,暫存器中的位可用來做不同的設置。透過指令 mrc 或是 mcr 讀寫 CP15 裡面的暫存器,mrc 是將 CP15 (c) 的暫存器讀至通用暫存器 (r); mcr 則反之。CP15 上某些暫存器實際上有多個實體暫存器,必須透過 opcode_2 指定要存取哪一個。例如,CP15:c0 有 MIDR (Main ID Register)、CTR (Cache Type Register)、TCMTR (TCM Type Register) 等等。 `MRC instruction<#目錄>`_ ----------------------- Move to ARM register from coprocessor. .. code-block:: @@~ MRC{cond} coproc, opcode1, Rd, CRn, CRm{, opcode2} MRC2 coproc, opcode1, Rd, CRn, CRm{, opcode2} where: +------------+-------------------------------------------------------------------------------------------------------------------------+ | cond | is an optional condition code (see Conditional execution). | +------------+-------------------------------------------------------------------------------------------------------------------------+ | coproc | is the name of the coprocessor the instruction is for. The standard name is pn, where n is an integer in the range 0-15.| +------------+-------------------------------------------------------------------------------------------------------------------------+ | opcode1 | is a coprocessor-specific opcode. | +------------+-------------------------------------------------------------------------------------------------------------------------+ | Rd | is the ARM destination register. If Rd is r15, only the flags field is affected. | +------------+-------------------------------------------------------------------------------------------------------------------------+ | CRn, CRm | are coprocessor registers. | +------------+-------------------------------------------------------------------------------------------------------------------------+ | opcode2 | is an optional coprocessor-specific opcode. | +------------+-------------------------------------------------------------------------------------------------------------------------+ function的設定可以參考 :http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.ddi0344k/ch03s02s01.html `MMU啟用流程<#目錄>`_ --------------- 1. **Disable Data Cache** .. code-block:: ARM assembly MRC p15, #0, r0, c1, c0, #0 BIC r0, r0, #0x00000004 MCR p15, #0, r0, c1, c0, #0   2. **Disable Instruction Cache** .. code-block:: ARM assembly MRC p15, #0, r0, c1, c0, #0 BIC r0, r0, #0x00001000 MCR p15, #0, r0, c1, c0, #0   3. **Disable MMU** .. code-block:: ARM assembly MRC p15, #0, r0, c1, c0, #0 BIC r0, r0, #1 MCR p15, #0, r0, c1, c0, #0 ; Clear MMU bit   4. **Invalidate TLB** .. code-block:: ARM assembly MCR p15, #0, r0, c8, c7, #0 ; r0 value will be ignored   5. **Setup Domain Access Control Register** * Domain: 支援16個domain,一個domain包含若干個sections。Domain可被設定成 (DACR, Domain Access Control Register): - No access (0b00): 只要存取該記憶體空間,則觸發Domain fault。 - Clients (0x01): 做存取權限檢查,也就是有可能產生Permission fault。 - Managers (0x11): 不做存取權限檢查,不會產生Permission fault。 .. image:: https://c1.staticflickr.com/1/455/19021371465_559888f426_o.png .. code-block:: ARM assembly LDR r0, 0x55555555 MCR p15, #0, r0, c3, c0, #0   6. **Setup Translation Table(設定TTBR0/TTBR1)** .. image:: https://c1.staticflickr.com/1/388/19024481031_e0d57672d6_o.png * **C** : Cacheable * **B** : Buferrable * **TEX[2:0]** : TEX Type Extension (TEX) bit 如下以SCTLR.TRE=0 (透過CP15的c1取得 System Control Register 32bits值的bit 28 來設定),也就是TEX Remap disabled模式,來說明記憶體區段屬性的配置與意義. .. image:: https://c1.staticflickr.com/1/519/18996675206_b1546ecfd2_o.png * **XN** : 為Execute Never 的屬性,若該記憶體分頁 XN Bit設定為1,表示該分頁不會被處理器Fetch指令進來執行,在Client Domain (也就是會稽核Access Permission狀態)下,記憶體分頁必須要 XN Bit為0,且記憶體屬性是設定為可讀取,同時沒有其他Prefech Abort發生的狀態下,才可以被執行. 如果該記憶體分頁是屬於Manager Domain,XN Bit就不會被當做稽核的條件. (所以就可以嘗試去執行,而不會導致例外發生). * **NS** : 這個屬性在支援 Trust Zone Security Extensions環境下,才會有作用. * **Domain** : 參考ARMv7的文件, VMSA() 會以4-bits表示Domain的Index,也就是說最大可以定義到16個Domain,每個Domain Index會依序對應到Domain Access Control Register 32-bits值中以各2-bits依序產生的16個欄位. * **S** : 用以定義該記憶體分頁是否為Shareable,S為 0 表示該記憶體分頁為Non-shareable,S 為 1 表示該記憶體分頁為 Shareable. * **nG** : 這屬性為Non-Global,用來定義該記憶體分頁是否為Global,如果nG為0,表示該記憶體分頁為Global,如果為1,表示該記憶體分頁屬於目前正在運作的ASID(Address Space Identifier),該值會對應到正在運作的Task (請參考CONTEXTIDR) * **Bit [18]** : when bits [1:0] == 0b10 - 0 Descriptor is for a Section. - 1 Descriptor is for a Supersection. 7. **Enable MMU** .. code-block:: ARM assembly MRC p15, #0, r0, c1, c0, #0 ORR r0, r0, #0x001 MCR p15, #0, r0, c1, c0, #0 ; Set MMU Enable bit   8. **Enable Instruction Cache** .. code-block:: ARM assembly MRC p15, #0, r0, c1, c0, #0 ORR r0, r0, #0x00001000 MCR p15, #0, r0, c1, c0, #0   9. **Enable Data Cache** .. code-block:: ARM assembly MRC p15, #0, r0, c1, c0, #0 ORR r0, r0, #0x00000004 MCR p15, #0, r0, c1, c0, #0