I'm looking on some information about bare-metal programming. I'm working on different powerpc platforms, and currently trying to prove that some tests are not impacted by the linux kernel. These tests are pretty basic, loads and stores in asm volatile, some benchmarks as well (Coremark, Dhrystone, etc). These tests run perfectly on Linux, but i now have to test them in baremetal, an environement i don't really have experience in. All my platforms have u-boot installed, and i'm wondering if there is such applications that would allow me to run my tests powerpc-eabi cross-compiled ? for example, would a gdbserver launched by u-boot be able to communicate via serial port, or ethernet ? Is it possible to have a busybox called by U-boot ?
3 回答
Uboot 是一个引导加载程序...使用它。您可能有一个带有 uboot 的 xmodem 下载器或 ymodem 下载器,如果迫不得已,您可以将您的程序变成一长串写入内存的单词,然后分支到那个。
uboot 已经设置了 ram 和串行端口,这就是你与 uboot 交谈的方式,所以你不必做所有这些。您不需要配置串行端口,但您需要了解如何写入一个字符,这意味着轮询状态寄存器以使发送器寄存器为空,然后将一个字符写入发送器寄存器。对字符串中的每个字符或要打印的任何内容重复此操作。
假设它是 C 的 C 程序的引导程序通常只涉及最低限度的设置堆栈指针(顺便说一下,uboot 正在运行,所以堆栈已经设置好,只要你加载你的程序,你就不能这样做不会与 uboot 正在做的事情发生冲突),然后分支到您的 C 入口点。
根据您编写高级语言程序的方式(我假设为 C),您可能需要将 .bss 区域归零并设置 .data 区域,这是使用引导加载程序将程序复制到 ram 的好处运行它,您通常不必执行任何操作,您下载并运行的二进制文件已经将 bss 归零,并且 .data 在正确的位置。所以它回来设置堆栈和分支,或者只是分支,因为你甚至可能不需要设置堆栈。
构建一个裸机程序是真正的挑战,因为你没有一个系统可以进行系统调用,这是一件很难放弃和/或模拟的事情。例如,newlib 让生活变得更轻松,因为它有一个非常容易替换系统后端,因此您可以将 printfs 留在 dhrystone 中(而不是删除它们并找到一种不同的方式来根据需要输出字符串或输出结果。
编译成 C 文件的对象很容易,组装程序集也很容易,而且你应该能够使用你的 powerpc-eabi gcc 交叉编译器来做到这一点,下一个挑战是链接,告诉链接器东西去哪里。因为这可能是一块平坦的 ram,所以您可以执行类似 -Ttext 0x123450000 之类的操作,其中数字是您要使用的 ram 的基地址。如果您有任何乘法或除法或任何浮点数或任何其他 gcc 库函数(替换您的处理器可能会或可能不会做的事情,或者需要包装器才能正确执行它们),或者任何 libc 调用,那么它将尝试将它们链接到. 理想情况下,gcc 库很容易,但取决于交叉编译器,它们可能是一个挑战,最坏的情况是使用 gcc 源并自己构建这些函数,
我强烈建议您反汇编您的二进制文件,并确保您的引导程序的入口点是否位于二进制文件的开头。使用 objcopy 制作二进制文件 powerpc-...-objcopy myprog.elf -O binary myprog.bin。然后在 uboot 提示符下使用 xmodem 或 ymodem 复制该程序并运行它。
备份。从该部件的数据表中查找 uart 并找出基地址时,您应该首先使用 uboot 提示写入 uart 发送寄存器的地址,例如,将 0x30 写入该地址,如果您有正确的地址然后在它在你的命令之后再次打印 uboot 提示之前,它应该在输出中有一个额外的零“0”。如果您无法通过 uboot 命令行的一次写入来完成此操作,那么您将无法使其在任何类型的程序中工作,您的地址错误或者您正在做其他错误的事情。
然后用汇编语言编写一个非常小的程序,通过写入该地址将字符输出到 uart,然后根据处理器的速度将其计数到某个大数字。如果您以 100Mhz 运行,则数到 1 亿或更多(或从几亿倒数到零)然后分支到开头并重复,输出,等待输出,等待。构建并链接这个小程序,然后用 xmodem 或其他任何东西下载并分支到它。如果您不能让它每隔几秒钟输出一个字符,那么您将无法进行更复杂的操作。
接下来小程序,轮询状态寄存器,等待tx缓冲区为空,然后向tx寄存器写入一个0x30。将保存 0x30 的寄存器增加到 0x31,并将该寄存器增加到 0x37。分支到等待 tx 为空并输出新值 0x31,使其成为无限循环。如果一旦您开始运行,您就看不到 01234567012345670... 永远重复而数字不会被破坏,它们必须是 0-7 并重复,那么您将无法进行更复杂的操作。
用一个小引导程序重复 C 中的最后两个程序,该引导程序分支到 C 入口点,如果你不能让那些工作,你将无法进一步进步。
从你认为不能没有的任何库调用开始(例如 printf),如果你不能做一个简单的 printf("Hello World\n"); 使用所有链接和系统后端等,那么您将无法运行 Dhrystone 并保留其系统调用。
编译器可能会将一些 Dhrystone 转换为您必须实现的 memcpy 或 memset 调用,这些最有可能手动调整的汇编版本,并且您的 Dhrystone 性能数字可以并且将受到这些函数的实现的巨大影响,所以你不能简单地这样做
void memset ( unsigned char *d unsigned char c, unsigned int len)
{
while(len--) *(d++)=c;
}
并期待任何表现。您可能会获取这些版本的 gcc lib 或 gnu libc 版本,或者只是从这些测试之一的 linux 构建中窃取这些版本(反汇编并获取 asm),这样您就有了苹果对苹果...
基准测试往往比真实更虚假,很容易在相同环境(Linux 或裸机等)中使用相同的编译器获取相同的基准测试源,并通过执行各种简单的事情、不同的编译器选项来显示截然不同的结果,重新排列功能,在引导程序中添加一些 nop 等。任何构建不同代码或利用 cahce 或受到 cahce 伤害的东西等。如果你想显示裸机比操作系统上更快,它如果没有一点工作,可能不会发生。您将需要获取 i 和 d 缓存,而 d 缓存可能需要您获取 mmu,依此类推。这些都可以是研究项目。然后你需要知道如何控制你的编译器构建,确保优化,如前所述,在引导程序中添加或删除 nops 以更改代码中紧密循环相对于缓存行的对齐方式。在操作系统上,有中断和事情正在发生,可能你正在处理多任务,所以使用裸机你应该能够让类似 dhrystone 的测试以与 linux 相同或更快的速度运行,如果你不能,不是因为 linux 更快这是因为您在裸机实现中没有做正确的事情。
是的,您可能可以使用 gdb 与 uboot 通信并加载程序,但不确定我从不使用 gdb,我更喜欢使用哑终端和 x 或 y 调制解调器,或者将 jtag 与 openocd 终端一起使用(telnet 进入 openocd 而不是 gdb 进入)。
您可以尝试将 Benchmarks 与 u-boot 一起编译。这样在 u-boot 完成加载后,它会加载您的程序。我知道这对于 ARM 平台是可能的。我不知道是否存在用于 powerpc 裸机开发的工具链
在此提交中的https://cirosantilli.com/linux-kernel-module-cheat/#dhrystone中,我提供了一个最小的可运行 Dhrystone 裸机示例,该示例带有在 QEMU 和 gem5 上运行的 ARM 上的 Newlib。有了这个起点,将它移植到 PowerPC 或其他 ISA 和真实平台应该不难。
在该设置中,Newlib 实现了除了系统调用本身之外的所有内容,如下所述:https ://electronics.stackexchange.com/questions/223929/c-standard-libraries-on-bare-metal/400077#400077这使得它更易于使用C 标准库的更大子集。
我通过使用crosstool-NG构建的工具链使用 newlib 。
该设置中的一些关键文件:
- 链接器脚本
- 系统调用实现
完整的 make 命令显示了一些使用的标志:
make \ -j 8 \ -C /home/ciro/bak/git/linux-kernel-module-cheat/submodules/dhrystone \ CC=/home/ciro/bak/git/linux-kernel-module-cheat/out/crosstool-ng/build/default/install/aarch64/bin/aarch64-unknown-elf-gcc \ 'CFLAGS_EXTRA=-nostartfiles -O0' \ 'LDFLAGS_EXTRA=-Wl,--section-start=.text=0x40000000 -T /home/ciro/bak/git/linux-kernel-module-cheat/baremetal/link.ld' \ 'EXTRA_OBJS=/home/ciro/bak/git/linux-kernel-module-cheat/out/baremetal/aarch64/qemu/virt/lib/bootloader.o /home/ciro/bak/git/linux-kernel-module-cheat/out/baremetal/aarch64/qemu/virt/lib/lkmc.o /home/ciro/bak/git/linux-kernel-module-cheat/out/baremetal/aarch64/qemu/virt/lib/syscalls_asm.o /home/ciro/bak/git/linux-kernel-module-cheat/out/baremetal/aarch64/qemu/virt/lib/syscalls.o' \ OUT_DIR=/home/ciro/bak/git/linux-kernel-module-cheat/out/baremetal/aarch64/qemu/virt/submodules/dhrystone \ -B \ ;