MCX N947成功初步移植Zephyr,標志著嵌入式技術的新飛躍,為物聯網應用注入更強動力與智能。
簡介
搭建 Zephyr 環境
參考Zephyr Getting Started。在這篇 Zephyr 的官方文檔中詳細介紹了環境的搭建過程, 同時 NXP 也提供了工具解決 Zephyr 安裝過程所需的依賴。
了解 Zephyr 架構
為了移植 Zephyr ,首先需要了解 Zephyr 的架構,理解其如何工作。在此處僅對其簡單介紹,便于理解后續內容。
構建系統
Zephyr 使用 CMake 作為其構建系統,使用 Kconfig 進行配置。首先 CMake 通過收集相應配置并執行對應腳本,生成對應的編譯腳本。
? ? ? ? ? ? ? ? ? ?Configuration Overflow
Devicetree
設備樹是一種描述硬件及其配置的樹形數據結構,如果對 MPU 開發領域有所深入對它可能會有所了解。Zephyr 使用設備樹選擇合適的驅動及配置。
使用設備樹可以在編譯階段確定硬件的配置是否合理,定義同一外設的多種不同工作模式便于切換, 同時使用設備樹也讓整個系統的復雜度增加。
設備樹的相關語法可參考 Zephyr Devicetree Syntax。
移植
對于適配新 SoC , Zephyr 官方目前資料不多。Zephyr ?SoC Porting,在該鏈接中簡述了移植 Zephyr 的要求 。
雖然資料很少,但移植工作也并非毫無頭緒。ZephyrGithub Repo本身便是一個詳細的參考內容。
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
?
整個文件結構可以參考同目錄下的其他文件夾。
Devicetree
soc 目錄中主要為 Kconfig ,其中主要定義了 SoC 中的各種屬性, 比如芯片的架構( Cortex-M33 )、有無 FPU ( CPU_HAS_FPU )等。
在完成 SoC 的移植后,我們需要定義 SoC 的相關外設,但在這個階段, 我們只需要對 FLASH 與 SRAM 做出定義即可,相應的驅動支持在后期進行適配。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 地址與大小,中斷優先級 BIT 數等等。
?
#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 中需要定義的內容不多。其中 soc.c 中需要定義芯片啟動后的流程,如時鐘初始化等等,在此處直接調用 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 已經提供了一份,其位于 zephyrproject/modules/hal/nxp 中, 但該版本中并沒有包括本次移植目標的相關驅動與配置文件,所以將我們下載好的 SDK 整理并入文件夾中。同時修改對應 CMake 文件,使項目能夠自動包含相關驅動文件。
Board
接下來定義 board ,只有定義 board 才能進行編譯測試。該過程以手上的 mcxn947-evk 為例。
最終創建的目錄結構如下,在這僅僅創建了全部必須的配置文件,包括相應的配置與設備樹, 為了方便測試,添加了 board.cmake ,該文件用來定義支持的下載方式與參數。
?
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)
?
到目前為止,我們仍未對時鐘、引腳、串口等驅動進行適配,所以無法通過串口輸出日志與 kprintf 。所以使用 RTT 進行輸出, RTT 通過向特定內存區域buffer讀寫,結合調試器達成數據傳輸, 在目前無法驅動串口(后續工作)的情況下很有用,相關配置在 mcxn947_evk_ns_defconfig 中體現。默認情況下, CPU 時鐘速度為 48MHz ,同樣在 Kconfig 中定義。
結論
在全部移植完成后,使用 samples/hello_world 例子進行測試。執行指令 west build -b mcxn947_evk_nssamples/hello_world 進行編譯。如果出現問題那就需要進行一些小修小改。
最終編譯結果如下:
?
[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%
?
因為在之前步驟中定義了下載方式,所以將硬件連接后直接執行指令 west flash 即可完成下載。
最后打開 J-Link RTT Viewer 觀察 RTT 內容。
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?RTT Output
至此, Zephyr 的初步移植工作已經結束,后續將會對常用外設進行移植。
評論