1. 熟悉sdk
SDK(Software Development Kit)是NXP针对其官方评估 版的软件开发包,可以在NXP的官网下载得到。SDK中包含了 各种程序范例,我们心心念念的固件库也包含在它里边。
-
NXP官网链接:https://www.nxp.com
-
未登录的用户需要先登录NXP官网,没有帐号的需要先注册。
-
登陆之后搜索所需要的SDK,下载即可。
1.1. SDK目录、文件简介
本书讲解的例程全部采用2.2版本SDK的库文件,以下内容请大家打开“SDK_2.2_MCIM6ULL_EBF6ULL”的SDK包配合阅读,如下所示。
1.1.1. boards:示例程序
SDK的boards目录包含了NXP官方评估版MCIMX6ULL-EVK的各种示例程序,非常有参考价值。
例如,下图是“boards\evkmcimx6ull”目录。
这些文件夹的说明如下:
-
demo_apps包含了一些应用范例,如串口打印“hello world”、使用lwip协议栈进行网络通讯等内容,具体如下图所示。
-
driver_examples包含了i.mx6主要片上外设的使用范例,非常详细。 在“boards\evkmcimx6ull\driver_examples”目录下包含各个外设的程序如下图所示。
-
project_template包含了官方示例使用的一些必备文件,这些文件主要是针对官方评估板做了一些引脚定义、时钟配置等功能, 具体如下所示。
-
rtos_examples包含了使用FreeRTOS实时操作系统的应用范例,具体如下所示。
-
usb_examples包含了各种USB程序示例,具体如下所示。USB设备种类繁多且驱动复杂, 参考官方的这些示例能快速建立自己需要的USB应用。
1.1.2. CMSIS:包含CMSIS标准相关的文件
在2.2版本的SDK中,CMSIS只有一个GCC 相关头文件,使用到时我们将会详细介绍。
1.1.3. CORTEXA:Cortex相关头文件
SDK目录下的CORTEXA文件夹包含了cortexa核适配iar、gcc的头文件,见下图。我们暂时不需要研究它们的作用。
1.1.4. devices:i.mx固件库
SDK中的devices目录包含的内容就是外设驱动固件库,该目录下的MCIMX6Y2文件夹即是针对开发板板载芯片对应的固件库,具体如下所示。
对此目录下的内容说明如下:
1.1.4.1. MCIMX6Y2目录下的全局文件
在MCIMX6Y2根目录下,可发现它直接包含一些文件,这些文件非常重要,是使用固件库编程最基础的部分,具体如下所示。
-
fsl_device_registers.h文件
fsl_device_registers.h文件的具体内容如下。
/*
*根据CPU型号包含相应的头文件
*在开发环境的全局宏定义中应根据CPU 指定芯片型号
*/
#if (defined(CPU_MCIMX6Y2CVM05) || defined(CPU_MCIMX6Y2CVM08) \
|| defined(CPU_MCIMX6Y2DVM05) || defined(CPU_MCIMX6Y2DVM09))
#define MCIMX6Y2_SERIES
/* CMSIS-style register definitions */
#include "MCIMX6Y2.h"
/* CPU specific feature definitions */
#include "MCIMX6Y2_features.h"
#else
#error "No valid CPU defined!"
#endif
固件库通常可以兼容很多型号的芯片,不同的芯片部分寄存器定义、芯片特性等内容可能会有差异。通过这样的条件编译代码,
就可以根据宏来包含不同的头文件,达到兼容不同芯片的目的了。当前仅支持一种芯片。
-
MCIMX6Y2.h文件
它主要是包含i.MX6芯片的各种寄存器定义和中断编号定义,是非常重要,非常基础的一个头文件。 所以在前面fsl_device_registers.h文件中就根据CPU型号把添加这个文件。该文件的部分内容如下。
MCIMX6Y2.h文件关于中断编号中的内容(部分)
typedef enum IRQn {
/* Auxiliary constants */
NotAvail_IRQn = -128,/**< Not available device specific interrupt */
/* Core interrupts */
Software0_IRQn = 0, /**< Cortex-A7 Software Generated Interrupt 0 */
Software1_IRQn = 1, /**< Cortex-A7 Software Generated Interrupt 1 */
Software2_IRQn = 2, /**< Cortex-A7 Software Generated Interrupt 2 */
Software3_IRQn = 3, /**< Cortex-A7 Software Generated Interrupt 3 */
Software4_IRQn = 4, /**< Cortex-A7 Software Generated Interrupt 4 */
Software5_IRQn = 5, /**< Cortex-A7 Software Generated Interrupt 5 */
Software6_IRQn = 6, /**< Cortex-A7 Software Generated Interrupt 6 */
Software7_IRQn = 7, /**< Cortex-A7 Software Generated Interrupt 7 */
/*以下省略*/
} IRQn_Type;
/*以下省略*/
此文件主要定义了一些关于i.MX6芯片特性的内容,例如你想知道本芯片有多少个IIS外设或UART外设,可以在本文件中找到,具体如下所示。在前面的fsl_device_registers.h的内容中也包含了本文件。
MCIMX6Y2_features.h文件中关于IIS、UART外设数量的定义
/* @brief I2S availability on the SoC. */
#define FSL_FEATURE_SOC_I2S_COUNT (3)
/* @brief IUART availability on the SoC. */
#define FSL_FEATURE_SOC_IUART_COUNT (8)
/* @brief SRC availability on the SoC. */
#define FSL_FEATURE_SOC_SRC_COUNT (1)
-
system_MCIMX6Y2.c/h文件
system_MCIMX6Y2包含一个源文件和头文件。其中头文件system_MCIMX6Y2.h中主要包含时钟定义以及源文件相应函数的声明,具体如下所示。
system_MCIMX6Y2.h文件中关于时钟频率的定义
/* 定义系统时钟值 */
2 #define DEFAULT_SYSTEM_CLOCK 528000000u
而源文件system_MCIMX6Y2.c中则主要包含系统初始化和配置系统时钟的函数。
-
MCIMX6Y2.xml文件
MCIMX6Y2.xml文件是NXP的开发环境需要的一些记录信息,此处不作介绍。
1.1.4.2. gcc、iar目录
在不同的编译平台下,使用汇编语言编写的启动文件、各种版本的分散加载文件的语法稍有区别,所以固件库把这些内容放在独立的文件夹。gcc和iar文件夹分别对应ARM-GCC和IAR开发环境,这些文件夹内的文件功能是一样的。以iar(IAR开发环境)为例,该目录下的文件具体如下图所示。
在iar文件夹下startup_MCIMX6Y2.s文件,主要用于配置存储器分配和提供芯片启动时运行的代码指令,其他几个.icf文件根据芯片型号以及芯片所接的存储器类型选择, “MCIMX6Y2xxxxx”指定芯片型号,_ram适配nandFlash和eMMC,本教程配套开发板应当选择这种.icf文件。_flash适配norFlash。.icf是程序的分散加载文件,使用到时再详细介绍。
1.1.4.3. MCIMX6Y2\drivers目录
drivers目录是固件库的主体,有时我们把这些文件称为外设驱动库,具体如下所示。
-
这些文件都使用fsl_xxx.c/h的命名格式,其中xxx是对应的片上 外设名字,如ADC、GPIO、UART、I2C等外设,大部分外设包含一个C源文件和头文件。
-
当使用到某个外设时,我们会把此处对应的外设驱动源文 件添加到工程中,加入编译,对于没有源文件的IOMUXC外设,则直接使用“#include”包含其头文件fsl_iomuxc.h。
-
特别地,其中的fsl_common.c和fsl_common.h中的common不是i.MX6芯片 的某个外设名字,它表示绝大多数工程都会需要这些“共同”的内容,所以一般工程都 会添加这个fsl_common.c文件,并且可以通过fsl_common.h文件包含前面介绍的fsl_device_regi sters.h头文件,达到最终包含具有大量寄存器定义的MCIMX6Y2.h核心头文件的目的。
1.1.4.4. MCIMX6Y2\utilities目录
utilities是实用工具集的意思,此目录下包含了开发常用的一些调试工具,如串口输出、运行日志,通常我们都会把这些文件加到工程以方便开发。
1.1.5. docs:部分说明文档
回到SDK的根目录,打开它的docs文件夹,可见到文件夹中包含如下图中的内容。
这目录下主要 是《Getting Started with MCUXpresso SDK for i.MX 6ULL Derivatives》文件 比较有用,它介绍了使用官方评估板时的基础使用步骤。文件夹 lwip、rtos、usb、分别保存对应的说明文档。文件夹MCUXpresso SDK API Reference Manual_MCIMX6Y2保存有.html格式的SDK API接口函数说明文档。
1.1.6. middleware:中间件
SDK中的middleware文件夹主要包含一些中间层软件,即这些软件常为应用层提供一些协议、架构上的支撑,它的主要部分又与芯片底层的硬件外设驱动(前面drivers目录中的文件)联系不甚紧密,是不同芯片之间通用的一些构件,具体见下图。
各个文件中间件的简要说明如下:
-
fatfs :这是一个嵌入式常用的文件系统,有了文件系统后能更好地管理存储器以及使用通用的文件形式来访问存储器。
-
lwip :这是嵌入式常用网络TCP/IP协议栈,使用协议栈可以方便地接入网络。
-
sdmmc :它是遵照SDIO协议编写的集成识别、读写SD存储卡功能的驱动。
-
usb :包含了遵照USB协议编写的基本驱动,在它之上可方便地编写USB应用程序。
1.1.7. rtos:实时操作系统
SDK目录下的rtos文件夹包含了FreeRTOS实时操作系统的源代码,位于“boards\evkmcimx6ull\rtos_examples”目录的FreeRTOS例程会使用到这里的源码,我们只需要知道源码再这里就可以了。见下图。
1.1.8. tools:开发工具
各个文件中间件的简要说明如下:
-
cmake_toolchain_files :提供cmake工具,用于生成arm gcc编译器需要的makefilew文件。
-
imgutil :提供图片压缩工具。
-
mfgtools 与 mfgtools-with-rootfs :提供MFG批量下载工具,
1.1.9. 其它
在SDK的根目录下还包含了几个.xml、.txt、.htm以及.bat文件,具 体见图 45-21。.xml和txt文件它们主要是包含SDK第三方构件的一些版 权、出处说明以及EVK-MCIMX6UL生成的一些说明信息,keilkill.bat批处 理文件用于清理编译程序生成的中间文件。
到此我们对imx6ull的sdk文件夹做了详细的介绍,不同版本的SDK文件夹可能略微不同,但总体的框架是一样的。
2. 编译下载官方SDK程序到开发板
上一章节我们简单介绍了SDK的文件目录结构,这一章节将介绍如何下载官方SDK程序到开发板中,
需要再次说明的是SDK是适配NXP官方评估板的,野火的开发板和官方的SDK存在着细微的差异,但是大部分还是兼容的, 尤其是设置为SD卡启动时,相似度更高。 本章也将会带领大家在linux下编译官方SDK中的”hello world”工程,并烧写到SD卡,最终在我们的开发板上运行。
本章内容并不讲解“hello word”程序如何实现,而是重点讲解在linux下编译SDK工程以及下载到开发板上。 即编译环境的搭建,编译固件、烧写到SD卡的操作流程。
学习目标:
-
能够搭建Linux下编译SDK工程的环境。
-
掌握SDK程序编译、烧写流程,能够烧写SDK例程到开发板。
配套源码以及工具:
-
NXP 官方SDK (路径:~/embed_linux_driver_tutorial_imx6_code/bare_metal/sdk_nxp )。
-
野火裸机下载工具download_tool (路径:~/embed_linux_driver_tutorial_imx6_code/bare_metal/download-tool/download-tool.tar.bz2 )。
2.1. SDK程序编译、烧写流程分析
学习本章请打开SDK源码目录。打开任意一个工程,可以看到每个工程有两个版本如下所示。
由于我们要在linux下编译、下载,所以我们需要选择armgcc版本工程。打开“armgcc”文件夹如下所示。
可以看到arngcc文件夹下包含很多build_xx脚本,这些脚本用于在linux环境下编译生成.bin 可执行文件, 所以我们需要一个arm gcc交叉编译器。CMakeList.txt用于生成编译过程中需要的Makefile,所以我们还需要一个CMake工具。 以.ld结尾的文件是链接器脚本相关文件,根据运行的脚本不同,脚本会自动调用相应的链接文件。
2.2. 在linux下搭建SDK编译环境
2.2.1. 安装CMake工具
执行以下命令:
sudo apt-get install cmake
2.2.2. 安装交叉编译工具
执行以下命令:
sudo apt-get install gcc-arm-none-eabi
添加临时环境变量,执行以下命令:
export ARMGCC_DIR=/usr
注:使用以上命令添加的环境变量只在当前终端上有效,如果虚拟机重启或者重新打开了终端需要再次执行添加环境变量命令。 该环境变量只有在编译官方SDK时才用得到,需要时动态添加即可。
2.2.3. 获取官方SDK文件
官方SDK提供了Windows版本和Linux版本,两者在功能上并没有什么差别,但编码上稍有不同,比如Linux下键盘的“Enter”是“n”,而Windows下键盘的“Enter”键是“rn”。为避免编码带来问题,我们选用Linux下的SDK。SDK可以参照上一章从NXP官网直接下载,也可以直接使用我们下载好的Linux版的SDK。
使用共享文件夹将“SDK_2.2_MCIM6ULL_RFP_Linux.run” 拷贝到Linux下,存放位置自定。然后运行.run文件生成SDK,若无法运行可尝试修改.run文件的权限,linux下的源文件命令如下。
./ SDK_2.2_MCIM6ULL_RFP_Linux.run
注:运行SDK_xxx_xxx.run时可能会出现输入SDK_xxx_xxx.run时“tab”键无法自动补齐,并且手动输入完整文件名也不能运行。原因大多是当前用户没有SDK_xxx_xxx.run文件的执行权限。使用命令 chmod +x SDK_xxx_xxx.run
修改SDK_xxx_xxx.run文件的执行权限即可。
SDK_xxx_xxx.run运行后会弹出图形化的界面,不过鼠标无法对界面上的选项进行操作,只能通过键盘上的方向键选择。例如在路径选择界面如下所示。
我们通过“上下”方向键选择生成的SDK文件保存位置。“左右”方 向键选择“Select”或“Abort Installation”。需要说明的是上图中选项1表示生成的SDK保存在你当前登录的用户文件夹下,不同用户路径不同。
SDK_xxx_xxx.run运行结束后会在我们制定的目录生成Linux下的SDK。
2.2.4. 执行编译
Linux下的SDK生成后,进入工程文件, “ SDK_2.2_MCIM6ULL/boards/evkmcimx6ull/demo_apps/hello_world/armgcc ” 运行build_ddr_release.sh脚本,命令如下:
./build_ddr_release.sh
build_ddr_release.sh用于生成*.bin文件,运行成功后会在当前文件夹下生成“ddr_release”文件夹, 在文件夹中存在一个sdk20-app.bin文件,将sdk20-app.bin放到SD卡程序并不能直接运行, 根据存储设备不同还要在sdk20-app.bin添加相应的头部信息然后才能在开发板上运行。
注:运行build_ddr_release.sh常见错误如下所示。
从错误提示不难看出,错误原因是没有添加ARMGCC_DIR环境变量,根据之前讲解添加环境变量命令“export ARMGCC_DIR=/usr”只在当前端口有效,所以在当前端口再次执行命令
export ARMGCC_DIR=/usr
添加环境变量即可(不建议将环境变量添加到系统环境变量中,因为该环境变量只有编译官方SDK程序才用的到, 后面章节主要是自己写程序,若编译还是错误,在运行 ./build_ddr_release.sh
前先运行 ./clean.sh
清除之前编译的内容)。
2.2.5. 烧写到SD卡
配套源码以及工具:
上一步生成的.bin文件并不能直接放到开发板上执行,因为缺少必要的头部信息。 在SDK中SDK_2.2_MCIM6ULL\tools\imgutil下readme.txt文件介绍了添加头部信息的步骤, 如果使用官方介绍的步骤会比较繁琐,我们提供了“embedfire_download”烧写工具 (路径:~/embed_linux_driver_tutorial_imx6_code/bare_metal/download-tool/download-tool.tar.bz2 )。 下面将基于该烧写工具讲解添加头部信息以及烧写步骤。
“embedfire_download”烧写工具集成了添加头文件和烧写到SD卡等步骤。详细介绍如下:
硬件要求:
-
一张空的SD卡,容量不限,使用前请提前备份您的SD卡内容,因为烧写时会直接写入扇区,SD卡中原来内容会被破坏。
-
一个读卡器。
2.2.5.1. 将内存卡连接到虚拟机
查看当前虚拟机上块设备的情况。
在将读卡器连接到虚拟机之后,再次使用lsblk查看,增加出来的设备就是我们内存卡的标识名。
2.2.5.2. 打开烧写工具执行烧写
将烧写工具拷贝到虚拟机,放到合适位置(我存放在家目录),使用以下命令对工具进行解压。
tar xvf download_tool.tar.bz2 -C ~
进入解压后的烧写工具目录, 修改“mkImage.sh”的权限(chmod 777 mkImage.sh),执行如下命令:
./mkImage.sh <需要烧写的文件所在路径>
烧写命令其实是运行 mkImage.sh脚本,并且将要烧写的.bin文件的路径作为参数,如下所示
./mkimage.sh ~/boards/evkmcimx6ull/demo_apps/hello_world/armgcc/ddr_release/sdk20-app.bin
将 ~/boards/evkmcimx6ull/demo_apps/hello_world/armgcc/ddr_release/sdk20-app.bin 替换成自己所生产的文件路径, 执行该命令后会列出可烧写的磁盘。如下所示。
没有确定哪个是SD,不要执行烧写!!!选错设备,可能导致电脑数据被破坏!!!!
在我的虚拟机上SD卡的设备名称是“sdb”所以输入“b”即可。如果是“sdc”则输入“c”,根据自己的SD卡设备名来确定具体的输入。
写入成功后会有以下提示
2.2.5.3. 实验现象
将开发板的启动选择开关设置为SD卡启动,使用串口调试助手链接开发板的串口1,将SD卡插入开发板, 启动后正常情况下可以在串口调试助手中看到开发板发出的
3. 开发前准备
在正式进入裸机开发之前我们必须了解两个知识点,i.MX6UL内存映射和i.MX6UL启动流程。
学习重点:
-
了解i.MX6UL内存映射
-
了解i.MX6UL的启动流程
3.1. i.MX6UL内存映射
i.MX6UL外设寄存器以及内存被映射到4G的地址空间,如下所示。
这里我们只关系内存映射,结合上图说明如下:
标号①,DDR映射区域。DDR作为i.MX6UL的主内存区域,它被映射在0x8000 0000地址处,最大空间为2G, 实际可用大小与选择的DDR芯片容量有关。
标号②,QSPI1映射区域,i.MX6UL可以外接串行norflash,norflahs映射到0x6000 000起始地址处。 norflash,支持“本地执行”作用类似于STM32的内部FLASH。NXP的跨界处理器RT1050系列 就是用这个外部norflah存储代码。因为野火开发板已经使用了容量更大的EMMC或nand flash作为主存储器, 所以配套开发板并没有外接norFlash
标号③,EIM外扩存储器映射区域。i.MX6UL可以通过EIM总线外扩存储器,我们这里没有用到,不做过多介绍。
标号④,启动引导程序(Boot ROM)内存映射区域,它位于芯片内部。启动引导程序(Boot ROM)位于起始 地址0x0000 0000 处。我们知道芯片上电后(或硬件复位)会自动从“0”地址处开始执行。和容易得出,这里 就是保存的启动引导代码。有关启动引导程序(Boot ROM)是如何工作的,我们将在下一小节详细介绍。
从上图我们可以知道i.MX6UL支持将多种存储器映射到4G的地址空间,我们只使用了DDR作为i.MX6UL的主内 存区域,起始地址为0x8000 0000 ,大小由实际使用的DDR芯片决定。启动引导程序(Boot ROM)位于“0”地址处,大小为96Kb。
3.2. i.MX6UL的启动流程
i.MX6UL支持多种启动方式,而具体硬件平台支持的启动方式与选择的存储器有关,本教程配套开发板支持nandflash(或EMMC)、SD卡启动、USB启动。本小节将介绍 教程配套开发板支持的启动方式,更详细说明请参考《i.MX 6UltraLite Applications Processor Reference Manual》第八章System Boot。
i.MX6UL完整的启动流程如下图所示,,完成启动任务的代码位于0x0000 0000 其实地 址处的Boot ROM。本小节将以“上电到程序运行”这条主线讲解i.MX6UL启动流程。
我们假设系统是正常的上电启动,那么启动流程过程大致可分为六步。①检查CPU的ID ,②检查复 位状态,③获取启动方式,④加载程序映像,⑤校验映像,⑥跳转到映像去执行。下面简要介绍着六个步骤。
3.2.1. 检查CPU的ID
系统上电或硬件复位后,CPU从0x0000 0000 地址开始 执行Boot ROM代码(以下简称为“Boot ROM代码”)。Boot ROM代码首先会检查CPU的ID。 这一步官方手册中也没有过多介绍,我们默认检测通过。
3.2.2. 检查复位状态。
一种情况是正常的硬件复位另外一种情况是从低功耗模式唤醒。先忽略低功耗模式唤醒的情况,继续向下执行。
3.2.3. 检查启动方式
在开发阶段我们可以通过设置i.MX6UL启动相关引脚的电平状态来改变芯片的启动方式。 BOOT_MODE[1:0] = 10B ,i.MX6UL将会从“内部”启动,所谓“内部”是相对于“Serial Download”来说的, “内部”可以认为是i.MX6UL支持的启动存储设备,例如emmc、nandflash、SD card、norFlash等等。 具体从那种“内部”设备启动,由BOOT_CFG1[7:4]决定,如下表所示。
表 内部启动方式选择
BOOT_CFG1[7:4] | Boot device |
---|---|
0000 | NOR/OneNAND (EIM) (本教程配套开发板没有用到) |
0001 | QSPI(norFlash启动) |
0011 | Serial ROM (SPI)(本教程配套开发板没有用到) |
010x | SD/eSD/SDXC(SD卡启动) |
011x | MMC/eMMC(emmc启动) |
1xxx | Raw NAND(nandflash启动) |
3.2.4. 加载程序映像
知道启动方式后Boot ROM代码并不能立即加载启动映像,因为启动映像(程序代码)保存在芯片外部存储设备, 从这些存储设备读数据之前首先要进行初始化。在Boot ROM程序根据保存在芯片中的默认配置信息配置这些存储器接口。
使用默认的配置信息配置存储器接口大多数情况下并不能发挥外设的最大性能, 还需要使用DCD(Device Configuration Data)进行二次配置。
DCD是一个表,表中包含了对各种外设(如 NOR Flash、DDR)的详细配置数据,它用于配置各种外围设备,例如NOR flash、DDR等等。
DCD 是保存在程序映像中的一部分。然而,Boot ROM 必须知道 DCD 的具体位置,才能加载并应用这些配置信息。Boot ROM怎么找到DCD呢? DCD地址信息保存在IVT(Image Vector Table)中,Boot ROM通过读取IVT得到DCD地址信息。
IVT是也是一个表或者说是一个数据结构,它保存有DCD数据的地址、程序的入口等等信息。 启动方式确定后Boot ROM从固定地址读取IVT。如下表所示。
表 IVT地址偏移
启动设备类型 | IVT偏移地址 | 初始加载区域大小 |
---|---|---|
NOR | 4 Kbyte = 0x1000 bytes | Entire Image Size |
OneNAND | 256 bytes = 0x100 bytes | 1 Kbyte |
SD/MMC/eSD/eMMC/SDXC | 1 Kbyte = 0x400 bytes | 4 Kbyte |
SPI EEPROM | 1 Kbyte = 0x400 bytes | 4 Kbyte |
从上表可知,IVT在存储器中的偏移值是固定的。以SD卡为例,如果从SD卡启动,那么IVG位于SD卡的0x400偏移地址处。
初始化完成后,Boot ROM便可以加载我们编写的程序。
3.2.5. 校验映像
在程序执行之前还要进行HAB校验。 HAB可以防止攻击者修改可编程内存中的代码或数 据区域,HAB校验更详细的内容请参 考《i.MX 6UltraLite Applications Processor Reference Manual》 8.3.1节High-Assurance Boot (HAB)。
3.2.6. 跳转到映像去执行
以SD卡启动为例,在执行跳转之前Boot ROM已经将代码从SD卡加载到了DDR,所以直接跳转到程序的入口地址即可。、
3.3. ARM工作模式与内核寄存器
3.3.1. ARM工作模式简介
为提高系统的稳定性,处理器会被分成多种工作模式,不同工作模式的权限不同。Cortex-M3或者M4内核的 的芯片分为特权模式和非特权模式,特权模式下CPU完全控制芯片而非特权模式下不能操作某些特殊 的寄存器。i.MX 6U作为一款应用处理器,将CPU工作模式进一步细分,支持九种工作模式下表所示。
表 处理器工作模式
处理器模式 | 编码 | 特权等级 | 执行(Implemented) | 安全状态 |
---|---|---|---|---|
User | 10000 | PL0 | Always | Both |
FIQ | 10001 | PL1 | Always | Both |
IRQ | 10010 | PL1 | Always | Both |
Supervisor | 10011 | PL1 | Always | Both |
Monitor | 10110 | PL1 | With Security Extensions | Secure only |
Abort | 10111 | PL1 | Always | Both |
Hyp | 11010 | PL2 | With Security Extensions | Non-secure only |
Undefined | 11011 | PL1 | Always | Both |
System | 11111 | PL1 | Always | Both |
结合上表处理器的各种工作模式讲解如下:
-
User 用户模式,用户模式是相对于Linux系统来说的,有Linux的情况下Linxu应用 程序运行在User模式,i.MX 6U特权等级从低到高 被分为3个等级(PL0~PL2),用户模 式属于PL0。用户模式下的执行有时被描述为“无特权执行”对系统来说User是安全的,User程序不会破坏系统。
-
FIQ快速中断模式,当发生FIQ中断后CPU就会进入FIQ模式,FIQ即“快速中断”,在中断章节我们将会详细介绍。
-
IRQ中断模式,当发生IRQ中断后CPU会进入中断模式,在中断章节我们将会详细介绍。
-
Supervisor管理模式 ,相比用户模式管理模式权限更高在,在该模式下 我们可以操作所有的寄存器。系统上电(复位)后CPU默认处于 该模式,我们的裸机程序也是运行在管理模式。
-
Monitor mode监听模式,就像后台服务,这个模式主要用来安全扩展模式,只用于安全
-
Abort mode 终止模式,CPU读取数据错误或者预取错误发生时将会进入终止模式。终止模式可以认为系统“挂了”。
-
Hyp mode 超级的监视模式,它主要用于一些虚拟化的扩展。
-
Undefined mode未定义指令异常模式,当CPU加载到一个无法识别的指令后将会进入该模式,同终止模式一样,是不正常的。
-
System系统模式,以系统模式执行的软件在PL1处执行。系统模式具有与用户模式相同的可用寄存器。
3.3.2. ARM内核寄存器
学过STM32或其他单片机的朋友对“寄存器”肯定不陌生, 但是我们接触的大多是“外设寄存器”。
- 外设寄存器是指微控制器或单片机中用来控制各种外设(如 GPIO、USART、ADC 等)的寄存器。
- 地址空间:外设寄存器本质上是处理器地址空间中的一块内存区域。通过访问这些内存地址,CPU 可以控制和配置外设的行为。这些寄存器是映射到特定的内存地址上的,开发者通过访问这些地址来操作外设。
- 与内存的关系:虽然外设寄存器映射到内存地址,但 CPU 不会直接在这些寄存器中进行数据运算,它们主要用于控制和状态监控。
与外设寄存器不同,内核寄存器并没有“地址”的概念,每款CPU的内核寄存是固定的
- 内核寄存器是直接位于 CPU 内部的寄存器,与外设寄存器不同,它们没有对应的内存地址。
- 固定数量和类型:每款 CPU 的内核寄存器都是固定的,数量有限。根据功能的不同,这些寄存器可以分为多种类型,如通用寄存器、状态寄存器、程序计数器等。
- 作用:内核寄存器用于存储 CPU 当前执行指令所需的操作数、中间结果、控制信息等。它们是 CPU 运算和控制的核心部分,能够直接参与数据的计算和逻辑操作。
i.MX 6内核寄存器分类
从应用程序角度看i.MX 6拥有13个32位通用寄存器R0~R12。4个32位特殊功能寄存器分别为 SP(栈指针寄存器)、 LR(链接寄存器)、PC(程序计数寄存器)和一个应用程序状态寄存器APSR。实际情况是不同的CPU工作模式有各自专用的寄存器, 不同CPU工作模式也有共用的一些寄存器,从系统角度看CPU寄存器下图所示。
注:标号①处,虚拟化扩展。仅在非安全状态下存在。
标号②处 ,安全扩展的一部分。仅在安全状态下存在。
上图列出了CPU各种各种工作模式下使的寄存器。
i.MX 6内核寄存器介绍
-
SP栈指针寄存器,处理器使用SP作为指向栈的指针,在ARM指令集中大多 数指令不能访问SP寄存器。ARM指令集提供了对SP寄存器访问的方法,使用到时我们再介绍。SP也可以 用作通用寄存器但是ARM官方不建议这样做。在程序中SP也和写作R13。
-
LR链接寄存器,链接寄存器用于保存中断或者函数的返回地址。当中断发 生时CPU会跳转到对应的中断服务函数中执行,而跳转之前CPU会自动将当前执行地址加4的地址 保存在LR寄存器中,中断服务函数执行完成后接着从LR指定的地址处执行。函数调用和中断类似,进入子函 数之前CPU自动将当前执行位置保存在LR链接 寄存器中,子函数返回后接着LR寄存器指定的地址处执行。LR链接寄存器又被称为R14寄存器。
-
PC 程序计数寄存器,程序计数寄存器可理解为“程序的执行位置”,当执行ARM指 令时,PC寄存器保存当前执行位置加8,即下一条指定的地址。当执行Thumb指 令时,PC寄存器保存当前执行位置加4,即下一条指定的地址。
-
R0~R12 通用寄存器,用于暂存、传递数据。从图 47-4不难看出所有工作模式共用R0~R7。在FIQ模式有自己的R8~R12,其他工作模式共用R8~R12。
-
PSR 程序状态寄存器,从图 47-4不难看出PSR稍微复杂,根据工作模式不同,它被分为APSR、CPSR、SPSR。下面简单介绍着三个寄存器。
(1) CPSR和SPSR,CPSR是当前程序状态寄存器(Current Program Status Register), SPSR是备份的程序状态寄存器(Saved Program Status Registers)。 SPSR的作用是当发生异常时备份CPSR的状态,也就是说SPSR保存的是执行异常处理函数前的CPSR的值。 在异常返回时CPSR可以从SPSR读回之前的状态。以下只讲解CPSR,SPSR与CPSR相同。
CPSR寄存器如下图所示:
结合上图部分配置为介绍如下,完整的内容请 参考《ARM® Architecture Reference Manual》B1.3.3 Program Status Registers (PSRs)。
M[4:0]模式位:
此字段保存处理器的当前模式,处理器工作模式请参考3.3.1小节的表格,M[4:0]字段对应表 47-3第二列的“编码”, 通过该位我们就可以知道处理器的当前工作模式。
T[5]和J[24]指令状态位:
此字段保存处理器执行的指令类型,分为ARM指令、Thumb指令、Jazele指令和ThumbEE指令。如下图所示。
A[8]、I[7]、F[6]屏蔽位:
A[8]是异步调试终止屏蔽位,进行程序调试时才会用到。I[7] ,IRQ中断 屏蔽位,I[7] = 1B,IRQ中断被屏蔽。同样F[6]是FIQ快速中断屏蔽位,F[6] = 1B,FIQ中断被屏蔽。这些寄 存器只能在PL1以及以上优先级的工作模式下才能修改,在任何工作模式下都可读。
E[9],字节顺序位:
该位用于设置字节序,E[9] = 1B 为大端格式,E[9] = 0B 为小端格式。
注:“大端”是指“高位”数据放在低地址处,“低位”数据放在高地址处,例如数据0x5566 保存在0x8000起始地址处,如果是大端格式则地址0x8000 保存的是“0x55”地址0x8001保存的是“0x66”。小端格 式正好相反,0x8000 保存的是“0x66” 址0x8001保存的是“0x55”。
(2) APSR(Application Program Status Register),与SPSR、CPSR类似,APSR保存CPU 执行状态,不同的是APSR权限受到限制,只有CPSR(或SPSR)的部分功能,如下所示。
从上图可以看出,相比CPSR寄存器,这里的某些字段变为“Reserved”即这些字段已经不可访问了。
ARM内核寄存器相关内容暂时介绍到这里,其他内容使用到时再详细介绍。