MCX N947成功初步移植Zephyr,標志著嵌入式技術的新飛躍,為物聯(lián)網(wǎng)應用注入更強動力與智能。
簡介
搭建 Zephyr 環(huán)境
參考Zephyr Getting Started。在這篇 Zephyr 的官方文檔中詳細介紹了環(huán)境的搭建過程, 同時 NXP 也提供了工具解決 Zephyr 安裝過程所需的依賴。
了解 Zephyr 架構
為了移植 Zephyr ,首先需要了解 Zephyr 的架構,理解其如何工作。在此處僅對其簡單介紹,便于理解后續(xù)內(nèi)容。
構建系統(tǒng)
Zephyr 使用 CMake 作為其構建系統(tǒng),使用 Kconfig 進行配置。首先 CMake 通過收集相應配置并執(zhí)行對應腳本,生成對應的編譯腳本。
? ? ? ? ? ? ? ? ? ?Configuration Overflow
Devicetree
設備樹是一種描述硬件及其配置的樹形數(shù)據(jù)結(jié)構,如果對 MPU 開發(fā)領域有所深入對它可能會有所了解。Zephyr 使用設備樹選擇合適的驅(qū)動及配置。
使用設備樹可以在編譯階段確定硬件的配置是否合理,定義同一外設的多種不同工作模式便于切換, 同時使用設備樹也讓整個系統(tǒng)的復雜度增加。
設備樹的相關語法可參考 Zephyr Devicetree Syntax。
移植
對于適配新 SoC , Zephyr 官方目前資料不多。Zephyr ?SoC Porting,在該鏈接中簡述了移植 Zephyr 的要求 。
雖然資料很少,但移植工作也并非毫無頭緒。ZephyrGithub Repo本身便是一個詳細的參考內(nèi)容。
SoC
首先為新SoC建立相應目錄
?
soc/arm/nxp_mcx/ |-- CMakeLists.txt |-- Kconfig |-- Kconfig.defconfig |-- Kconfig.soc `-- mcxnx4x |-- CMakeLists.txt |-- Kconfig.defconfig.mcxn947_cpu0 |-- Kconfig.defconfig.mcxn947_cpu1 |-- Kconfig.defconfig.series |-- Kconfig.series |-- Kconfig.soc |-- linker.ld |-- soc.c `-- soc.h
?
整個文件結(jié)構可以參考同目錄下的其他文件夾。
Devicetree
soc 目錄中主要為 Kconfig ,其中主要定義了 SoC 中的各種屬性, 比如芯片的架構( Cortex-M33 )、有無 FPU ( CPU_HAS_FPU )等。
在完成 SoC 的移植后,我們需要定義 SoC 的相關外設,但在這個階段, 我們只需要對 FLASH 與 SRAM 做出定義即可,相應的驅(qū)動支持在后期進行適配。mcx n947 是一顆 Cortex-M33 架構的 CPU , 芯片使用了 Cortex-M33 定義的 TrustZone 功能, 將外設分為 Secure 和 Non-Secure 兩部分。因此,在定義設備樹時,需要考慮安全外設與非安全外設的不同地址。
?
// dts/arm/nxp/nxp_mcxn947_common.dtsi
?
// 在該 dtsi ( device tree source include )文件中僅定義到目前為止移植所需要的外設
// 包括 SRAM 地址與大小, FLASH 地址與大小,中斷優(yōu)先級 BIT 數(shù)等等。
?
#include#include / { cpus: cpus { #address-cells = <1>; #size-cells = <0>; cpu@0 { compatible = "arm,cortex-m33f"; reg = <0>; }; cpu@1 { compatible = "arm,cortex-m33"; reg = <1>; }; }; }; &sram { #address-cells = <1>; #size-cells = <1>; sramx: memory@4000000 { compatible = "mmio-sram"; reg = <0x4000000 DT_SIZE_K(96)>; }; srama: memory@20000000 { compatible = "mmio-sram"; reg = <0x20000000 DT_SIZE_K(32)>; }; sramb: memory@20008000 { compatible = "mmio-sram"; reg = <0x20008000 DT_SIZE_K(32)>; }; sramc: memory@20010000 { compatible = "mmio-sram"; reg = <0x20010000 DT_SIZE_K(64)>; }; sramd: memory@20020000 { compatible = "mmio-sram"; reg = <0x20020000 DT_SIZE_K(64)>; }; srame: memory@20030000 { compatible = "mmio-sram"; reg = <0x20030000 DT_SIZE_K(64)>; }; sramf: memory@20040000 { compatible = "mmio-sram"; reg = <0x20040000 DT_SIZE_K(64)>; }; sramg: memory@20050000 { compatible = "mmio-sram"; reg = <0x20050000 DT_SIZE_K(64)>; }; sramh: memory@20060000 { compatible = "mmio-sram"; reg = <0x20060000 DT_SIZE_K(32)>; }; }; &peripheral { #address-cells = <1>; #size-cells = <1>; fmu: flash-controller@43000 { compatible = "flash-controller"; reg = <0x43000 0x1000>; #address-cells = <1>; #size-cells = <1>; status = "disabled"; flash0: flash@0 { compatible = "soc-nv-flash"; reg = <0x0 DT_SIZE_M(2)>; }; }; }; &nvic { arm,num-irq-priority-bits = <3>; }; // dts/arm/nxp/nxp_mcxn947_ns.dtsi // 在 ns ( None-Secure )配置中定義外設與 SRAM 的基礎地址 / { soc { sram: sram@4000000 { ranges = <0x4000000 0x4000000 0x20000000>; }; peripheral: peripheral@40000000 { ranges = <0x0 0x40000000 0x10000000>; }; }; }; #include "nxp_mcxn947_common.dtsi"
?
Soc.c
以目前的進度, soc.c 與 soc.h 中需要定義的內(nèi)容不多。其中 soc.c 中需要定義芯片啟動后的流程,如時鐘初始化等等,在此處直接調(diào)用 SystemInit 圖個方便。nxp_mcxnx4x_init 中定義了芯片初始化行為, 以目前的進度,還不需要做什么特殊操作,暫時留空。
?
#include#include #include #include static int nxp_mcxnx4x_init(void) { return 0; } #ifdef CONFIG_PLATFORM_SPECIFIC_INIT void z_arm_platform_init(void) { SystemInit(); } #endif /* CONFIG_PLATFORM_SPECIFIC_INIT */ SYS_INIT(nxp_mcxnx4x_init, PRE_KERNEL_1, 0); #ifndef _SOC__H_ #define _SOC__H_ #endif /* _SOC__H_ */
?
Misc
在移植過程中,還需要使用 MCUX SDK , Zephyr 已經(jīng)提供了一份,其位于 zephyrproject/modules/hal/nxp 中, 但該版本中并沒有包括本次移植目標的相關驅(qū)動與配置文件,所以將我們下載好的 SDK 整理并入文件夾中。同時修改對應 CMake 文件,使項目能夠自動包含相關驅(qū)動文件。
Board
接下來定義 board ,只有定義 board 才能進行編譯測試。該過程以手上的 mcxn947-evk 為例。
最終創(chuàng)建的目錄結(jié)構如下,在這僅僅創(chuàng)建了全部必須的配置文件,包括相應的配置與設備樹, 為了方便測試,添加了 board.cmake ,該文件用來定義支持的下載方式與參數(shù)。
?
boards/arm/mcxn947_evk/ |-- Kconfig.board |-- Kconfig.defconfig |-- board.cmake |-- mcxn947_evk_ns.dts `-- mcxn947_evk_ns_defconfig # Kconfig.board config BOARD_MCXN947_EVK_CPU0 bool "NXP MCXN947-EVK [CPU0]" depends on SOC_SERIES_MCXNX4X select SOC_PART_NUMBER_MCXN947VDF # Kconfig.defconfig if BOARD_MCXN947_EVK_CPU0 config BOARD default "mcxn947_evk_cpu0" if BOARD_MCXN947_EVK_CPU0 endif # BOARD_MCXN947_EVK_CPU0 # mcxn947_evk_ns_defconfig CONFIG_SOC_SERIES_MCXNX4X=y CONFIG_SOC_MCXN947_CPU0=y CONFIG_BOARD_MCXN947_EVK_CPU0=y CONFIG_UART_CONSOLE=n CONFIG_CONSOLE=y CONFIG_RTT_CONSOLE=y CONFIG_USE_SEGGER_RTT=y CONFIG_SYS_CLOCK_HW_CYCLES_PER_SEC=48000000u /dts-v1/; #include/ { model = "NXP MCX N947 EVK"; compatible = "nxp,mcxn947"; cpus { /delete-node/ cpu@1; }; chosen { zephyr,sram = &srama; zephyr,flash = &flash0; }; }; &flash0 { partitions { compatible = "fixed-partitions"; #address-cells = <1>; #size-cells = <1>; boot_partition: partition@0 { label = "mcuboot"; reg = <0x0 0x00008000>; }; slot0_partition: partition@8000 { label = "image-0"; reg = <0x00008000 0x00010000>; }; }; }; # board.cmake board_runner_args(jlink "--device=MCXN947") include(${ZEPHYR_BASE}/boards/common/jlink.board.cmake)
?
到目前為止,我們?nèi)晕磳r鐘、引腳、串口等驅(qū)動進行適配,所以無法通過串口輸出日志與 kprintf 。所以使用 RTT 進行輸出, RTT 通過向特定內(nèi)存區(qū)域buffer讀寫,結(jié)合調(diào)試器達成數(shù)據(jù)傳輸, 在目前無法驅(qū)動串口(后續(xù)工作)的情況下很有用,相關配置在 mcxn947_evk_ns_defconfig 中體現(xiàn)。默認情況下, CPU 時鐘速度為 48MHz ,同樣在 Kconfig 中定義。
結(jié)論
在全部移植完成后,使用 samples/hello_world 例子進行測試。執(zhí)行指令 west build -b mcxn947_evk_nssamples/hello_world 進行編譯。如果出現(xiàn)問題那就需要進行一些小修小改。
最終編譯結(jié)果如下:
?
[133/133] Linking C executable zephyrzephyr.elf Memory region Used Size Region Size %age Used FLASH: 10844 B 2 MB 0.52% RAM: 4944 B 32 KB 15.09% IDT_LIST: 0 GB 2 KB 0.00%
?
因為在之前步驟中定義了下載方式,所以將硬件連接后直接執(zhí)行指令 west flash 即可完成下載。
最后打開 J-Link RTT Viewer 觀察 RTT 內(nèi)容。
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?RTT Output
至此, Zephyr 的初步移植工作已經(jīng)結(jié)束,后續(xù)將會對常用外設進行移植。
評論