RPI3 B+ - Pi 乐园探险记 第 3 部分( 更新支持 Pi1 & 2)





5.00/5 (11投票s)
Raspberry Pi 挫折和探险系列第三篇
*** 在下面的旧源代码中,RGBA 宏中的红色和蓝色是颠倒的 .. 抱歉,没注意到
引言
上一篇文章
好吧,这并不是最初打算的文章,原本是关于如何在裸机上运行 USB 集线器,并连接键盘和鼠标。虽然我在 Pi1 上实现了这一点(正如我在上一篇文章中添加的更新截图所示),但我圣诞节收到了一份礼物,即一个 Pi3 B+ 和一个 Pi 摄像头模块。
终于有了这样的板子,我立刻去尝试我的代码,结果就是鸡飞狗跳。不仅我的 Pi3 特定代码不起作用,我根本无法在 Pi3 上运行任何裸机代码,这为我花了三周时间找出原因的极度沮丧埋下了伏笔。
我还要提醒一句,和许多 Pi 相关的东西一样,有些答案我实在不知道,它们隐藏在未公开的细节中。
这是我运行代码的结果。我们有一些视觉上的板子细节,以及一些运行的图形测试(IFS 蕨类植物、矩阵雨、着色),以及闪烁活动 LED....好吧,我知道这不是很令人兴奋。
背景
Pi3 B+ 使用 1.2GHz 64 位四核 ARMv8 CPU,但是 Arm 启动固件会强制 CPU 以 32 位模式运行,并且它运行的默认 Linux 发行版也是 32 位。在我让所有东西都工作起来之后,我发现将 CPU 切换到 64 位模式非常简单,我可能会在以后的文章中进一步探讨。对于这篇文章,我们将让 CPU 以 32 位模式运行,因为目的是要有一套代码可以在任何 Pi 版本上运行。
Pi 基金会据称已经做了特定的固件加载顺序,理论上是为了让所有 Pi 版本兼容。我之所以说是据称,是因为我们即将讨论的,我无法让它工作。有些细节缺失了。所以固件启动顺序应该是这样的加载顺序,进行一些内存重排,然后从地址 0x8000 启动
RPi3 启动顺序
- 检查 kernel8.img,如果找到,则加载它并以 64 位模式启动。
- 如果未找到,检查 kernel8-32.img,如果找到,则加载它并以 32 位模式启动。
- 如果未找到,检查 kernel7.img,如果找到,则加载它并以 32 位模式启动。
- 如果未找到,检查 kernel.img,如果找到,则加载它并以 32 位模式启动。
这个顺序是正确的并且奏效了,但对我来说不起作用的是从 0x8000 地址读取处理器。我无法从该地址可靠地启动任何裸机镜像。我尝试了几乎所有网上声称可以在 Pi3 上运行的裸机代码,但都失败了。我甚至一度认为板子坏了,但用 raspbian 镜像快速测试一下,它就又活了过来。甚至来自 David Welch (https://github.com/dwelch67/raspberrypi) 等网站的代码也无法让 Pi3 正常工作。在论坛上搜索,很明显我不是一个人。我们有很多人无法让 Pi3 B+ 正常工作。快速测试表明处理器根本没有从 0x8000 启动或开始,或者即使启动了,固件也没有正确地将我的文件放入内存。
更新:Pi 社区中的一些人对我暗示 Pi3 加载器存在问题或 bug 表示不满,甚至说我是骗子。他们坚持认为 XYZ 代码或仓库在 Pi3 上没有问题。我只能说,我手头的板子似乎在处理大多数从 0x8000 开始的代码时遇到了困难,并且加载非常不稳定。我没有理由再花费数小时来处理这个问题,因为加载器并没有提供任何比使用 config.txt 文件从 0x0 开始处理器更多的额外功能,我能看到的。
经过很多挫折,我终于找到了一个网站,上面的代码可以启动 Pi3,而且他的所有示例都有效。这个网站 (https://github.com/PeterLemon/RaspberryPi) 是由 Peter Lemon(又名 krom)创建的。他的所有代码都做了一件事,就是在 SD 卡上引入了一个 config.txt 文件,其中包含以下几行:
kernel_old=1
disable_commandline_tags=1
disable_overscan=1
framebuffer_swap=0
第一行是最重要的一行。它告诉固件在内存地址 0x0
启动处理器。我的代码从 0x0 开始加载的那一刻,一切都爆发了。我真的不知道这是否是固件的一个 bug,可能会很快修复,或者是一个永久性的问题。Peter Lemon 没有详细说明他是如何或为何知道这样做的。我现在只能说,目前它是必需的。所以你的 Pi3 裸机 SD 卡上最终会有 4 个文件:bootcode.bin、start.elf、config.txt 和 kernel8-32.img。kernel8-32.img 也可以是 kernel7.img。如果查看上面的启动顺序,两者之间似乎没有太大区别。我使用的是 Kernel8-32.img,这样我就知道它是 Pi3 文件,将 kernel7.img 留给 Pi2 内核文件。
已更新:我被告知 fixup.dat 需要放到 SD 卡上,否则 Pi3B 将只报告 256MB 内存。我们玩的示例程序 ninguno 接近那么多内存,所以如果你打算编写更大的代码,我会让你自己决定是否添加它。
已更新:新的复合 SD 镜像包含 3 个内核文件:kernel.img (Pi1)、kernel7.img (Pi2) 和 kernel8-32.img (Pi3)。SD 卡现在将有 6 个文件,但无论放在哪个 Pi 板上都能工作。
Using the Code
Arm 工具已更新。它们已从上篇文章中的旧 5.4 版本升级到 6.2 版本。在我遇到麻烦时,我更新了我的工具以防万一 (https://developer.arm.com/open-source/gnu-toolchain/gnu-rm/downloads)。
与之前的文章一样,我只是像这样设置一个批处理文件来编译我的代码:
g:\pi\gcc_pi_6_2\bin\arm-none-eabi-gcc -O2 -mfpu=neon-fp-armv8 -mfloat-abi=hard
-march=armv8-a+crc -mtune=cortex-a53 -nostartfiles -g3 -Wa,--defsym,BCM2837=1 start.s main.c -Wl,
-T,rpi3.ld -o kernel.elf -lc -lm
你可以看到处理器选项与 Pi3 上的 Arm8 处理器匹配
-mfpu=neon-fp-armv8 -mfloat-abi=hard -march=armv8-a+crc -mtune=cortex-a53
SRC 2a 更新:新的源代码 2a 不需要将符号推送到 Start.S 文件,它会自动检测 CPU 类型和外设基地址并进行纠正。该代码中包含的批处理文件不会推送符号,因此要小心不要混合使用原始 zip 文件和 2a zip 文件之间的代码。
有一个新的命令会将 CPU 推送到 Start.S
中的汇编代码
-Wa,--defsym,BCM2837=1
它将 "BCM2837=1
" 推送给 Start.S
的汇编器,用于控制是否包含某些汇编块(更新:Pi1 BCM8235=1, Pi2 BCM8236 =1
)。这是为了准备将代码与前几篇文章中的标准 Pi1 代码重新合并,因为我需要 start.S
来添加/删除代码,具体取决于我们正在编译哪个 Pi。
Start.S
知道 Pi2/Pi3 是四核的,它启动并让核心 1、2、3 进入死循环。只有核心 0 继续运行并在此示例中进入主代码,所以它看起来确实像一个 Pi1
。同样,所有这些都是为了让我能够将 Pi3 代码合并回旧文章的代码中。
更高级的用户可能想打开 Start.S
并查看在跳转到 C 起始点(在 main.c 中称为 kernel_main
)之前运行的汇编代码。
已更新:在原始代码中,需要在初始 C 代码中立即运行两个关键代码段。将 Start.S
与运行 Pi1/Pi2 的能力合并后,我能够将这两个关键代码段移到汇编代码中。新的 Start.S
既清除了 .BSS,又运行了自动外设基地址检测,以及一项新功能:读取 Arm 处理器的 CPUID
并将结果存储在两个全局变量中。
RPi_IO_Base_Addr = Peripheral Base Address auto detected (0x3F000000 or 0x20000000)
RPi_CpuId = ARM processor CPUID
你可以通过声明一个外部变量来在 C 代码中访问它们,如下所示:
extern uint32_t RPi_IO_Base_Addr; // The base IO address auto-detected by Start.S
extern uint32_t RPi_CpuId; // Detected CPU by Start.S
稍作修改的源代码现在将显示检测到的外设基地址以及 CPU。
这是我 Pi3
B+ 的详细信息:
与 Pi1
的详细信息进行比较:
SRC 2a 更新:技术上讲,Src 2a 生成的 Arm6 代码在单个 Kernel.img 中可以运行在所有 PI 上,这得益于所有自动检测代码。但它在 Pi2 和 Pi3 上运行速度较慢,因为它没有使用它编译 Arm6 时可用的高级指令。如果你想尝试一下,只需有一个文件 Kernel.img,你就会看到这种行为。这是一个非常嵌入式的技巧,我们通常会用它来创建一个引导加载程序,它会搞清楚我们在处理什么板子等等,然后下载一个正确的镜像文件给那个板子。
如上所述,新的 SD 复合镜像为每个 Pi 板提供了 3 个内核,并且上面讨论的启动顺序将确保该卡在任何 Pi 上都能工作。
我提供了 3 个批处理文件:Pi1.bat(构建 kernel.img)、Pi2.bat(构建 Kernel7.img)和 Pi3.bat(构建 Kernel8-32.img),它们将为每个不同的 Pi 板生成相应的 Kernel 镜像文件。我将正在处理的版本复制到 make.bat,我的编译器会与之链接。
有一个特殊的链接器文件 "rpi3.ld",它设置了起始地址、.BSS 段和 .end,定义了堆的起始位置。它相当直接,任何对 GCC 简单链接脚本的阅读都会解释它的作用。
我试图让这篇文章保持简短,因为它实际上只是一个临时报告,但对于那些专门尝试 Pi3 裸机的人来说,它可能很有用。
关注点
有了 Pi3
,我现在有更多未来可能会研究并撰写文章的主题:
- 使用所有 4 个核心
- 64 位编码
- 摄像头裸机访问
希望下一篇文章将按照计划进行,关于使用键盘和鼠标驱动程序的 USB 裸机访问,并在 Pi1 和 Pi3 上进行测试。
历史
- 版本 0.4:添加了新的 2a 版本源代码,具有扩展的自动检测代码
- 版本 0.3:添加了 Pi 社区中关于 0x8000 加载和 fixup.dat 的异议。
- 版本 0.2:代码修改为运行 Pi1 & 2(
Start.S
已大量修改) - 版本 0.1:初始发布