电子大神的日记本,供应链专家的功夫茶盘,在这里记录、分享与共鸣。

登录以开始

Kinetis L系列的启动过程及其实现(一)

Kinetis L系列的启动过程及其实现(一)

 

[前提]
众所周知, Kinetis L 的 MCU 不存在 bootloader, 这与 NXP 的 ARM 系列有明显的不同.
在 NXP 的 LPC 系列(ARM7)中, 存在 bootloader, 这个 bootloader 将执行一系列的初始化, 也会将 flash 中断向量表 copyto ram, 同时也包括定义的 ram function. 而且, 在 bootloader 的启动中, 还会比较 ISP PIN(一般这个 PIN 是 P0.14), 如果 P0.14 被拉低, 那么 LPC MCU 将进入 ISP 状态, 也就是初始化 uart, 并可以通过 uart 直接下载 firmware.
而 Freescale 的 Kinetis L serials(CORTEX MO+) 是没有类似的 bootloader 的存在. 这可能带来两个方面的不同效果的影响:
作为项目设计者, 我们不得不自行设计系统 startup 的过程, 一手操办 system reset 中初始化的代码, 比如说, 数据初始化过程, 中断向量表与 ram function 向 RAM 的 copyto 等等. 没有经过设计者包办的启动过程, 我们甚至无法正确的运行 main() 函数. 一方面, 这带来了我们工作量的增加; 但是另一方面讲, 这也确实是一个机会, 使得项目设计者, 清晰了解 KL MCU 中的 RESET 过程, 并必须尊重 MCU 的 startup 的规则.

 

[参考]
网络上, 至少有两种中文开发者参考资源, 描述了 Kinetis L serial 的 startup 的过程, 但是都未曾明确指出, 这个 startup process 描述的出处 -- 实际上, 它来源于官方的 Freescale 的可下载帮助文档, 文档名为:
Kinetis L Peripheral Module Quick Reference 之中第一章: Chapter 1: General System Setup(Software Considerations)

 

[BOOT过程]
我们使用 IAR 6.50.3, 并借鉴 IAR 自带的 FREEDOM KL25Z 的 SAMPLE(FOR KL25Z128) 来说明 BOOT 过程, 不意外地, 通过对 BOOT 过程的理解, 以及 startup 规则的要求, 我们将跟随完成 startup 实际代码, 建立一个成功 BOOT 过程:
(1) START POINT:

FLASH 的起始地址被定义为: 0x00000000, 初始 stack pointer(SP) 位于向量表的首地址, 而 4-byte offsets 位置也就是向量表的第二个量是 Program counter(PC).

设备支持从 internal flash 做 boot, 也就是从地址 0x00000000 开始, 获得 SP 与 PC(VECTOR_001) 后, 并跳转于 PC address 开始执行指令.

当然设备也只从从 RAM 启动, 比方说, 如果在默认从 flash 启动后, 我们在 startup 中完成, 将 flash 中的异常向量表 copyto 到 ram, 那么我们将能实现从 RAM 启动的过程. 另外要提到的是, 我们记得曾经了解过, 将异常向量表在 startup 中 copy头 ram, 不仅仅可以 do ram boot, 也可以加速 interrput 的过程.

那么不意外的, 从向量表的 3rd 量开始, 这将是 interrupt isr 的入口地址(图例1中, 我们见到3rd 的值为 VECTOR_002, 实际上是不可屏蔽中断 NMI 对应的地址, 而之后的 VECTOR_003 开始, 是我们 interrupt 例程中使用到的地址. 在之后的图例中, 我们将看到对应的注释).
[图例1: 来自 freedom sample 中的 vectors.c 中, 对异常向量表的定义]

 

 

(2) 异常向量表的ADDRESS:

让我们检查 IAR complier 的 linker 安排, 无疑的, 异常向量表的初始地址是: 0x00000000
[图例2: 来自 IAR Linker 脚本文件, sample 中为, 128KB_Pflash.icf]

 

在 IDE 中, 我们使用 config options - > linker 的 config TAB 中, 对 ICF file 的 edit 按钮也可以读到:
[图例3: 来自 IAR IDE 中操作的 ICF FILE 编辑框]

准确的, Linker 脚本文件, 定义异常向量表的起始地址, 就是 FLASH 的起始地址:0x00000000
[图例4: 来自 IAR Linker 脚本文件, sample 中为, 128KB_Pflash.icf]

 

(3) RESET 过程与 MCU 状态

在 RESET 过程中, MCU 的 digital I/O PIN 将进入 disable state, 也就是高阻态(内部上拉下拉禁止). analog PIN 将进入默认的 analog function. 而一旦 RESET 完成后, digital I/O PIN 继续保持 disable state, 中断被禁止, 并且大部分的 modules 被关闭. 时钟模式进入 FEI 模式. 且 watchdog 被激活.
对于 kinetis L serial, 上述 RESET 执行后的 device state, 则暗示着我们, 我们需要在 startup 中主动设计: core clock, system clock 以及 flash clock (事实上, 默认 flash 与 bus 的 clock 相同). 这是 kinetis L serial 赋予我们设计者的神圣使命(其实, 如果不完成这些任务, 我们事实上无法正常启动这颗 MCU). ;P

 

(4) low-level 的汇编(启动)代码的实现

通过图例1, 我们已知, 设备(默认)从 FLASH 执行后, 从起始地址 0x00000000 处开始, 载入了 4-byte 的 SP 值后, 并将位于 0x00000004 的 PC 值读取后, 跳转到 PC 值指向的指令执行地址, 进行指令执行. 向量表中, PC 对应的定义 VECTOR_001.
[图例5: 来自 freedom sample 中的 vectors.h 文件]

上面的图例清楚说明了, 我们需要实现 __startup 对应于 (VECTOR_001), 也就是 PC 的执行地址. 我们需要实现 __startup 函数, 以实现 MCU 的正确 BOOT.
[图例6: 来自 freedom sample 中的 crt0.s 文件]

 

在上面的图例中, 我们见到了 __startup 的建立, 简单来说, 内容如代码注释, 通用寄存器被清零, 启用中断后, 跳转到了 C 代码形式的 start() 函数. 当然, 对于任何一个 IAR 构建的 NULL KINETIS L project, 这可能意味着, 我们不得不遵守完成 __startup 这个事实上的程序入口点.(题外话, 此时要想编译正常通过, 我们也需要定义 start()函数, 当然在函数内部我们需要 call main() ).

 

(5) REBUILL ALL 得到 OUTOUT FILE

这样, 我们在 IAR IDE 中, 通过正确设定 target mcu 后, 即可 rebuild all, 并生成输出文件格式(设为二进制文件 .bin).  在 IAR 下, 也就是设定:
[图例7: IAR IDE 中的 out file format 的设定]

 

(6) 为何 CODE 的起始地址是: 0x000000C0

默认从 FLASH 启动的 .bin 文件, 将异常向量表放在最前, 即从 0x00000000 的地址开始. 跟随着异常向量表, 才是 CODE 的放置区. 异常向量表的 size 有多大?
[图例8: NVIC 的定义, 来自于: Kinetis L Peripheral Module Quick Reference 之 NVIC 章节]

在 kinetis sample 中, 我们也找到下面的证明:
[图例9: NVIC 的定义, 来自 freedom sample 中的 vectors.c 文件]

[图例10: NVIC 的定义, 来自 IAR Linker 脚本文件, sample 中为, 128KB_Pflash.icf]

这里, 48*4 = 0xC0.

 

Kinetis L系列的启动过程及其实现(一)

Kinetis L系列的启动过程及其实现(二)

Kinetis L系列的启动过程及其实现(三)

 

 

博主
allen_zhan@163.com
allen_zhan's BLOG
allen_zhan's BLOG
点击跳转