Kinetis L系列的启动过程及其实现(三)
(10) 在 start() 中建立 system clock
(10.1) FEI (core/system default clock 21MHz, bus/flash clock 10.5MHz)
任何系统的建立, 都离不开 system clock 的确立. 在 Freescale 的 Kinetis KL serial 中, clock 控制器是 MCG. 无论是否建立 clock, 系统首先进入 FEI, 利用内部 IRC 建立振荡, 这意味着系统可靠性的提高, 外部振荡激励成功与否, 都不会妨碍系统 BOOT 代码的执行.
Tip2: 系统 RESET 后首先进入的状态
当我们没有建立任何 clock 时, 与没有内部 RC 振荡器的 MCU 不同. KL25Z 的 MCG 控制器首先建立的是内部 RC 振荡的 FEI 模式. 从图例18 中, 我们可以得知无论我们利用内部 or 外部振荡构建怎样的模式, 我们首先进入的是 FEI 状态.
[图例18: MCG mode state diagram, From Reference Manual]
在论坛上, 我们见过某个答复, 认为 FEI 即系统默认的 core clock 是 internal slow IRC(32K) 倍频后得到的 20.97MHz. 我们觉得这个回答是欠妥的, 因为在 24.5.2 章节(of Reference Manual) 提到的是, 只有在 FEE 或者 FBE 模式下, 并从外部的 32.768k 振荡参考下, 根据默认的 FLL 倍频因子, 才得到 20.97M.
实际上, 我们觉得, 正确的 SYSTEM BOOT 后, KL25Z 进入 FEI, 是内部大约 32KHz 经过FLL(默认) 640 倍乘因子的结果, 也就是如图例19中提到的, 是 RESET 后 的 21MHz.
[图例19: RESET 后默认进入 FEI模式(MCG模式中的一种), 内部低速 IRC 32K 倍频后得到 21M Hz(Core/system clode)]
Tip3: 关于 bus clock 的测量.
对于 Freescale 的 KL25Z 系列, 时钟有三种类型:
core/system clock: 无疑的, 在上面 Tip2 中, 我们提到了系统 RESET 默认进入的 FEI 的 core clock 是 MCGOUTCLK 也就是 21M Hz.
bus clock: 总线时钟与外围相关, 比如 SPI, 比如 COP, 比如 Timer(在Freescale World 中被称为 PIT)
flash clock: FLASH clock(这里与 bus clock 相同, From Renference Manual).
默认的, bus/flash clock 总是 system clock/2, 除非有必要时, 我们对 SIM_CLKDIV1 在启动后的程序内进行操作 OUTDIV4.
因此我们计算得到 bus clock 在 RESET 进入 FEI mode 后, 应该是: 21M/2 = 10.5M.
对于 bus clock 的测量, 我们可以使用指定 PIN 的 clkout, 在 KL25Z 中它是 RC3. 我们可以用示波器测试到这个频率.
(10.2) 利用 internal IRC 建立 freescale KL serial(cortex-m0+) 最大之 48MHz
如果我们不利用 external osc, 我们完全可以建立在 internal IRC 之基础上的最大 48MHz 的 system clock. 正如 sample project 我们见到的, 利用 inernal IRC slow 32KHz, 在设定 1464 FLL 倍频后, 我们得到48Mhz.
那么一个质朴的问题就是, 利用内部的 IRC, 我们建立的 48M core clock, 是否符合 USB 的通讯标准? 在一份查询到的论坛问题中,可以利用这个 clock source 实现 USB, 但是不被推荐. 让我们在这里引出这个帖子的讨论地址, 留作参考:
https://community.freescale.com/thread/305970
(10.3) 利用外部 8M 晶振建立 48M system clock
(a) 在 pll init 之前, 设定 DIV1, 通常为1, 表明被 MCG 控制器生成的 MCGOUTCLK 直接等于 core/system clock, 没有分频. 设定 DIV4, 通常是2,表明 (bus/flash clock) = (core/syste clock) /2.
(b) 确认保持在 FEI 状态中.
(10.4) 从 FEI -> FBE (system clock 8M under current 8M crystal, bus clock 4Mhz)
(a) 根据图例18, 从初始状态, RESET后默认进入 IRC 之 32k 的 FLL 倍频得到 21M Hz core clock, 这是 FEI mode. 我们最终希望获得外部晶振 8MHz 的PLL 倍频到达 48MHz 的 PEE mode.
(b) 根据图例18, 也就是 Kinetis 的 MCG 特性, 我们不得不首先从 FEI mode -> FBE mode. 我们倾向认为 FBE mode 是一种过度阶段, 核心就是利用 external crystal(8M), 通过 div 因子, 获得 32K 左右频率, 可以替代原有的 FLL 倍频模式(也就是 FEI mode). 具体的说, 就是我们必须保持使用 external crytal 8M clock, 通过分频得到 FBE mode 下需要的 31.25 ~ 39.0625KHz. 这个变化, 核心就是设定 MCG_C1 register, 对于 8M external crystal, 恰好设定分频因子为 256, 我们计算得到: 8M/256 = 31.25k Hz, 满足 FBE下的(31.25~39.0625Khz) 需求.
(c) 因此, 在对 C1, C6, C2 register 进行相应设定后(from reference manual), 特别是我们上面提到的 C1 之分配因子, 我们进入了 FBE mode(利用 external crystal 8m 的分频之后获得 FLL 的倍频).
(d) 最终的, 我们计算 core/system clock 现在应该是: 31.25(内部 show IRC 置换为 外部8M分频) * 640 = 20MHz, 这应该就是 FLL 倍频后的结果.
(e) 应当注意到, 设定 FEB 模式后, 应等待 crystal osc 初始化完成, 我们用 MCG_S register 可以检查基于external crystal 的 osc 是否建立. 并应检查是否 external reference clock 已作为 FLL source, 也检查 MCGOUTCLK 是否选用了外部 reference clock.
(f) 另外, 在脱离默认的 FEI 后, 进入 external crystal 分频后(32.25KHz)再 FLL 倍频得到 FBE mode 后, 我们应该设定 CME, 如果外部频率脱离了当前设定 high freq(3~32MHz, from reference manual), 则控制器 RESET.
(g) 应注意到, 因 C6[PLLS] 使用 FLL as MCGOUTCLK source, 并且默认 RESET 后C5[PLLCLKEN] 为0, 表 MCGPLLCLK 未使能, 故在 FBE 状态下, PLL 模式disable, 系统仍然处于 FLL 模式下.
(h) 那么, 是否这里 system clock 可以合理假设为: 由于 FLL 倍频被使用, 则是 32.25K*640 = 20MHz 呢?
答案是: 并非如此, 我们理解, 尽管这里 FLL 倍频产生, 而其输出不被使用. 这样, MCGOUTCLK(这里是 core/system clock) 就不是 FLL 倍频的结果, 而是 OSCCLK, 也就是 8Mhz, 同时 bus/flash 为 4MHz.
Tip4: 如何理解 FBE?
FEI: FLL Engaged Internal, 这是系统 RESET 后进入的默认模式, 我们已经读出了关键字, FLL, internl, 从 slow IRC(32KHz) 中进行 FLL 我们获得了默认 21Mhz 的 system clock(10.5MHz bus clock).
FBE: FLL Bypassed External, 这里的关键字被我们上面的分析所熟知, 我们利用 external reference clock, 这里是 8M crystal 的分频(分频后进入了 31.25 ~ 39.0625KHz 参数域后), 替代 FEI 的 slow IRC 进行了 FLL 倍频.
但是我们饶有兴趣地注意到, 这里 FLL 倍频尽管在工作, 但是最终的 OUTPUT(MCGOUTCLK, 这里是 system clock) 却没有使用到 FLL 倍频, 简单说是 FLL OUTPUT 被 BYPASS.
而最终的 system clock, 是 external crystal 的直接输出. 这里就是外部晶体频率为 8M Hz, 我们合理假设在 FBE 的 bus/flash clock 也就是 8/2 = 4Mhz.
(10.5) 从 FBE -> PBE (system clock 8M under current 8M crystal, bus clock 4Mhz)
(a) 根据图例18, 为了最终抵达 PEE mode, 我们在 FBE mode 下, 需要进入 PBE mode.
(b) 对于 Freescale Kinetis 的复杂繁琐的 MCG 的状态管理, 我们颇有微词. 但是也不得不遵守(因为 Freescale 的手册教育说, 这是建立 system clock 的必须过程```).
那么, 当我们理解, 我们之前, 成功从 external reference clock 完成了 FLL(尽管不用实际输出被bypass), 也就是从 FEI->FBE. 那么此时在 FBE->PEB(PLL Bypassed External) 过程开始前, 从关键字的分析中, 我们已经判断出 PBE 又是一个过渡mode, 其目的是将 external reference clock 的 FLL 模式更换为 PLL 模式.
(c) 选择合适的分频, 产生 PLL reference clock 在 [2, 4]Mhz, 这里我们选择 4. 选择合适的 VCO 倍频, 这里我们选择 24. 因此, 计算得知, PLL 倍频的结果是: (8M/4)*24 = 48M Hz
(d) 应注意, 启用 PLL(同时将禁止 FLL), 也需要跟踪状态, 等待启用PLL 的同步过程完成. 也应等待 PLL 的锁定完成.
(e) 同样的, 在 PEB(PLL Bypassed External) mode 下, 尽管 PLL 倍频锁定成功, 我们 bypass 其输出, 而 system clock 仍取 OSCCLK 为 8MHz(bus clock 4MHz).
(10.6) 从 PBE -> PEE (system clock 48M under current 8M crystal, bus clock 24Mhz)
(a) 根据图例18, 我们需要最终抵达 PEE mode.
(b) 在完成了 PBE, 也即是成功PLL 倍频并锁定后, 我们开启 PLL OUTPUT 作为 system clock(也就是 MCGOUTCLK) 即可完成全部过程.
(c) 应注意, 开启 PLL OUTPUT, 我们仍然应在 MCG_S register 中等待确认 PLL 输出启用成功.
(10.7) Reference Manaul 给出的 4M crystal -> PEE 的流程例子
尽管使用的 crystal 值不同, 且使用了 high gain 设定等小小差异, 我们仍然可以几乎完整地比拟为我们的 setup clock 的 例子. 参考图例20:
[图例20: 来自 RM 手册的例子流程图]
11. 完成 system boot 的 setup
至此, 我们完成了 system boot 的过程. 值得一提的是, 等我们把 size 的优化级别提到最高, 然后构建一个 NULL 内容的 main(), 生成 .bin 文件的大小是: 0x944(大约 2k bytes). 这就是说, 最小系统初始化的 size 的代价. (我们没有任何的调试输出, 产生多余字节的 code).
[图例21: NULL main() 的最小系统的 size 的 sample]
[总结]
1. 我们在 Freescale kinetis 的 L serials, 以 KL25Z128 为例, 对 system boot 的过程进行了解析, 这个完整的过程, 如上所述, 包括:
(1) 定义汇编代码, 进入 __startup 程序入口点. 要提及我们在汇编中, 初始化了通用寄存器, 并使能了中断, 完成了 start c代码的函数入口点.
(2) 在 start 函数中, 我们进行了 RAM 初始化, 并对 .data 进行初始化. 如前所述, 这个过程核心是将 flash 中异常向量表 copyto RAM. 如果有定义的 ram function, 也在这里完成copy.
(3) 系统 clock 的建立, 我们详述了从 system 内部的 IRC 默认 FLL 倍频的 FEI mode, 通过 FEI -> FBE -> PBE -> PEE, 最终在 8MHz crystal 的 external reference clock, 使用 PLL 倍频, 完成了 kinetis 的 cortex-m0+ 的最大允许 48M core/system clock 以及 24M bus/flash clock 的过程.
[感想]
(1) 完成一份 BLOG 的过程, 好比作为列车长, 引领观光的旅客们, 共同完成这段曼妙的旅程.
在终点站台上, 与各位珍重道别之际, 我想感谢各位的阅读,
那么也要对这份匆匆急就的 BLOG中, 可能的错误致歉.
(2) FREESCALE 的 MCU, 与 NXP 之类同等 ARM(cortex-m) 比较, 似乎略显繁琐, 熟悉与上手的进度都会嫌慢.
一方面, 我们少少郁闷着这种不便(诸如这种可怕的 MCG 的 clock 的建立过程).
另一方面, 因其彻底服从开发者的初始化过程, 使我们对 IDE 的 LINKER 脚本, 对程序进入点, 对 memory space 的形象构建, 以及对 SYSTEM RESET 的初始化过程的完全掌控, 都有着与比如说 NXP ARM 的内置 bootloader 机制的较大不同.
因此, 在这个完成 system reset 的过程后, 于恍恍惚惚间, 于此专业领域, 我们似乎若有所得...
Allen Zhan
allen_zhan@163.com
2013.08.04 于深圳福田
Release On EETC
allen_zhan_752827529 2013-8-5 14:58
用户1602177 2013-8-5 14:55
allen_zhan_752827529 2013-8-5 12:38
用户1610239 2013-8-5 09:07