逐飞 RT1064 库 GCC (VSCode) 移植踩坑

2021-05-13

Summary: 该文介绍了在 MacOS 下,使用 GCC 工具链在 VSCode 中利用 Cortex Debug 插件编译调试逐飞 RT1064 库的踩坑过程。(Linux 下类似,但是具体步骤本文并没有写)

本文所记录的是我整个过程的踩坑经历,关于项目和其使用说明请参考项目主页:hilookas/SeekFree_RT1064_Library_GCC_Porting | GitHub

关于逐飞 RT1064 RT-Thread 库的移植项目请参考其项目主页:hilookas/SeekFree_RT1064_RTThread_Library_GCC_Porting | GitHub

从山外 K60 库开始,我开始了我的漫漫智能车之路。山外库只支持 IAR,但是可惜 IAR 并不支持 MacOS/Linux,逐飞 RT1064 库增加了对 Keil 的支持,但是 Keil 仍然只支持 Windows。我不甘心与在 Mac 上与嵌入式无缘,同时好在开源工具链 GCC 支持(几乎)所有平台,故有了这篇折腾文章。

单片机相关

现在的微控制单元(单片机)一般会有两个文档,一个是 Data Sheet ,负责介绍芯片电气相关的参数,另一个是 Reference Manual ,负责介绍硬件上一些功能如何使用,寄存器布局等。

初次之外厂商提供一些 Application Note,用于介绍特定的功能是如何使用,这个文档一般比较口语化,而且部分还提供中文翻译,推荐要实现一个功能的时候可以优先参考。

NXP 提供 SDK ,SDK 提供了许多样例供参考,和一些芯片的基础库,将寄存器封装起来,避免用户自己直接操作寄存器(太头疼)。

CMSIS 是一个 ARM 的标准,用于标准化来自不同厂商的 ARM 芯片差异。将芯片的 ARM 内核相关的寄存器封装起来。

CMSIS 还提供 CMSIS Pack,其中包含了不同厂商自己外设的驱动程序。

NXP 的 SDK 包含了 NXP 的 CMSIS Pack (SDK 的 SDK_2.9.1_MIMXRT1064xxxxA/devices/MIMXRT1064 里的内容其实就是 NXP 的 CMSIS Pack 的)

ref:

核心板启动基本流程

RT1064 芯片有内嵌的 BootROM (直接刻在芯片上的那种),其会根据启动引脚和内部保险丝的情况决定从哪个位置启动。同时 RT1064 芯片内部有一颗使用 FlexSPI2 的 Flash (即 RT1064 的 4 意义所在),没有特殊配置,芯片会从这个 Flash 启动。

BootROM 会从 Flash 的 IVT(Image Vector Table) 读取 BootROM 配置信息(如 PC 指针的值,和配置 SEMC SDRAM 的“脚本”)

ref:

芯片的寄存器

寄存器有两种,一种是处理器寄存器,ARM 核心使用,一种是外设寄存器,用于控制芯片中的外设的(如 UART)。后者被映射到处理器的内存空间中,可以被寻址,具体地址参见芯片手册。

ref:

统一文件编码为 UTF-8

逐飞的库文件编码是乱的,强迫症的我不能对此坐视不管。库里的绝大多数文件是使用 GB2312 编码,但是有两个文件是同时使用了 UTF-8 和 GB2312 编码,更加神奇的是,还有三个文件中参加了几个中文符号!这导致 iconv 的转换编码进程被卡住。这主要涉及到以下几个文件:

UTF-8 和 GB2312 编码混在一起:
Libraries/seekfree_libraries/common/SEEKFREE_PRINTF.c
Libraries/seekfree_libraries/common/SEEKFREE_PRINTF.h
混有中文标点符号:
Libraries/nxp_libraries/middleware/usb/host/class/usb_host_cdc.c
Libraries/nxp_libraries/drives/fsl_semc.h
Libraries/nxp_libraries/utilities/debug_console/fsl_debug_console_conf.h
本来就是用 UTF-8 编码的:
./Project/RT1064智能车推荐引脚分配.txt
./Project/CODE/本文件夹作用.txt

为了加快修改所有源代码文件编码为 UTF-8 的进程,我简单的写了一个脚本,供各位参考:

#!/bin/bash

# Convert GB2312 to UTF-8 (MacOS) and CRLF to LF
# from https://docs.moodle.org/310/en/Converting_files_to_UTF-8
# from https://gist.github.com/jappy/2012320
# change * to *.c to specify only code files
# for x; do
# 涉及到 *.c *.h *.s *.S *.txt
find . -type f -name "*" | while read x; do
  iconv -f GB2312 -t UTF-8 < "$x" | tr -d '\015' > "$x.tmp"
  mv "$x.tmp" "$x"
done

改了 deceive typo

Libraries/nxp_libraries/deceive 目录名错了,是 device

IAR 和 Keil 的配置也做了相应的调整

确定需要编译的文件

逐飞库里并不是所有的文件都需要编译的,有些文件加入编译后会导致编译不通过(如引用的头文件不存在),甚至导致运行时错误(错误覆盖了某些函数),具体可以参见 files.cmake (根据 IAR program/RT106X.ewp 配置文件进行了注释)

但是,相对于 IAR 的配置,有些文件是仍旧需要的,否则会导致编译不过(引用的头文件不存在)。以下头文件是需要的:

Libraries/nxp_libraries/xip/
Libraries/nxp_libraries/CMSIS/Include/
Libraries/nxp_libraries/CMSIS/Driver/Include/

ref:

移植链接文件

链接文件负责将代码合适的摆放在芯片所需要的位置上(如下文提到的 IVT 必须放到指定位置上,芯片才能正常启动)

MIMXRT1064xxxxx_seekfree.ld 文件修改自 NXP SDK 的 SDK_2.9.1_MIMXRT1064xxxxA/devices/MIMXRT1064/gcc/MIMXRT1064xxxxx_flexspi_nor.ld

逐飞核心板所需要的内存布局可以参考 IAR 的链接文件 Project/IAR/icf/MIMXRT1064xxxxx_flexspi_nor.icf 和逐飞的 Libraries/doc/read me.txt 说明文档。

ref:

关于链接的加载地址(LMA)与运行地址(VMA)

一段代码的加载地址并不一定与运行地址相同,比如,在程序运行前,先从 ROM 里加载代码到 RAM 中,再在 RAM 中执行代码,速度会比直接从 ROM 中执行快很多。GCC Linker 默认情况下,加载地址为运行地址,但是其也支持自行使用 AT 执行指定加载地址。

ref:

加载地址设置这里有坑,设置了 AT 之后的代码好像并不会自动与前面的代码错开放置,需要自行计算排好。

Linker output.map 是一个很好的调试工具

ref:

移植启动文件

主要涉及到芯片上电启动后 Reset_Handler 和,在其中运行的 SystemInit

需要配置 DTCM 和 ITCM 的内存分配,以及复制全局变量等到对应的位置。

Libraries/nxp_libraries/startup/GCC-ARM/startup_MIMXRT1064.s 文件修改自 NXP SDK 的 SDK_2.9.1_MIMXRT1064xxxxA/devices/MIMXRT1064/gcc/startup_MIMXRT1064.S

同时还修改了 Libraries/nxp_libraries/device/system_MIMXRT1064.cLibraries/seekfree_libraries/common/common.h (代码放置位置的快捷宏)

ref:

解决 GCC 并不帮助我初始化全局变量的问题

IAR 提供了 __iar_program_start 函数,拷贝在 icf 链接文件中定义的需要拷贝的数据。GCC 提供的相同地位的函数 _start 好像不帮我拷贝那些 LMA 与 VMA 不同的块。

解决方案:在 startup_MIMXRT1064 手动把变量初始化了。

ref:

移植 CMake 文件

NXP SDK 提供的样例是是使用 CMake 编译的,逐飞的核心板由于其外置 RAM ,需要手动开启一些编译 Flag,具体可以参见 flags.cmakeLibraries/doc/read me.txt

CMakeLists.txt*.cmake*.sh 修改自 NXP SDK 的 SDK_2.9.1_MIMXRT1064xxxxA/boards/evkmimxrt1064/demo_apps/led_blinky/armgcc 下相关文件。

ref:

关于 GDB 直接使用不好使的问题

在使用 Cortex Debug 之前,我有尝试直接使用 GDB (通过 JLinkGDBServer)一段时间,但是 GDB 直接使用好像不是特别好使,continue monitor halt 很烦,并且使用 GDB 的自带寄存器显示功能 info registers 的显示好像是错的。Cortex Debug 用完后,感觉还不错,就没有再继续研究 GDB 了。

ref:

解决 JLink 调试器启动芯片后外置 RAM 访问出错的问题

现象是使用 Cortex Debug 插件启动芯片后,程序会第一次死在 Libraries/nxp_libraries/device/system_MIMXRT1064.c 的复制向量表(到外置 RAM)过程中,进入 HardFault 中断,使用 JMem 查看后发现对应位置(0x80000000 外置 RAM)没有数据(显示为 -)。在 Reset_Handler 中设置断点的情况下,芯片自动重启后会恢复正常(进入到 main 函数)。

芯片自带的 BootROM 负责根据内置 Flash 上的 IVT 区域的 DCD(Device Configuration Data) 配置信息配置 SEMC 与外置 RAM 的通信。猜测 JLink 启动芯片的时候并不会运行芯片自带的 BootROM ,故需要 JLinkScript 在每次启动芯片的时候,手动配置一下外置 RAM 相关的数据。

使用从 NXP SDK 中提取到的 evkmimxrt1064_sdram_init.jlinkscript (来自 SDK_2.9.1_MIMXRT1064xxxxA/boards/evkmimxrt1064/sdmmc_examples/sdcard_interrupt/evkmimxrt1064_sdram_init.jlinkscript),附加到 Cortex Debug JLink 启动选项(文件 launch.json)后,可以正常调试运行了。

ref:

关于 CMSIS-DAP

CMSIS-DAP 可以用 pyOCD 与其通讯。在 MacOS 下,运行 pyOCD 与 CMSIS-DAP 通讯是“免驱”的。OpenOCD 好像也可以与它通讯,但是没有继续尝试...另外根据 Cortex Debug 的文档,Homebrew 的 OpenOCD 版本比较老,推荐使用编译方式安装或者直接从官网下载

ref:

解决 Cortex Debug 调试界面,外设寄存器无法正常显示的问题

复制 RT1064 的 SVD 文件即可

MIMXRT1064.xml 即 SVD 文件来自 NXP SDK 的 SDK_2.9.1_MIMXRT1064xxxxA/devices/MIMXRT1064/MIMXRT1064.xml ,也是 NXP CMSIS-Pack 的 NXP.MIMXRT1064_DFP.13.0.0.pack/MIMXRT1064.xml

ref:

解决没有初始化无线的时候,无线中断处理函数内无法正常打断点的问题

rt,经过测试发现,如果没有调用初始化函数,Cortex Debug 反汇编根本无法找到中断处理函数,只有在初始化后才能找到。故认为是如果没有调用初始化函数,编译器自动优化掉了处理函数。

在使用断点功能的时候需要注意并不是所有位置打了断点都能生效的。

ref:

JLink 和核心板之间的连接

DIO CLK GND 是必须连接的,VCC 可以连接(这四根引脚即为核心板调试引脚最靠近 Type-C 口的那四个),RST 实测不连也可以 Reset (该功能不依赖该引脚)

如果有转换板的话,板子上其实有一个四线的口,用那个口即可

使用串口调试

核心板上调试口的串口(标注 TX RX 字样),默认是要连到 UART1_TX_B12 UART1_RX_B13 两个脚上,但是实际上空了两个 0R 电阻位作为跳线。如果需要使用调试口上的串口,需要手动用一坨锡或者电阻连上。或者在主板上引出这两个口

Tips:修改 Project/USER/inc/RT106X_config.h 可以配置默认串口。 Tips:使用 screen /dev/tty.xxx 115200 可以连接到串口

ref:

Cortex Debug 参考