首页 | 新闻 | 新品 | 文库 | 方案 | 视频 | 下载 | 商城 | 开发板 | 数据中心 | 座谈新版 | 培训 | 工具 | 博客 | 论坛 | 百科 | GEC | 活动 | 主题月 | 电子展
返回列表 回复 发帖

PowerPC 的仿真和交叉开发

PowerPC 的仿真和交叉开发

有些开发人员可能没有机会亲手操作 PowerPC® Linux™ 系统(不过,您可以买一个这样的系统,在我撰写这篇文章的时候,它的价格不超过 200 美元)。对于有好奇心的 x86 Linux 用户,仿真是一种方便而又廉价的选择。现在至少有三种开源 PowerPC仿真器,其中有两种还相当新。
精确度有些仿真器,尤其是专业开发人员使用的那些仿真器,都是周期精确(cycle-accurate)的,也就是说,对于一个给定上下文环境下的特定指令,它在仿真器中花费的周期与它在真实硬件上运行所需的周期完全一致。这些仿真器不仅可以仿真指令集,而且还可以仿真内部的管道和处理器的缓存。在拥有实际芯片之前的开发过程中,这些仿真器特别有用。与硬件性能计数器收集到的信息相比,仿真器让开发人员可以更深入地洞悉性能瓶颈。然而,这些仿真器也有一些严重的局限性。由于它们在编制文档时对知识产权和硬件技巧有太多的要求,因此想免费查看或修改其内部结构几乎是不可能的。相反,处理器设计人员会提供一些可用的二进制文件,有时候它们是免费的,但通常只适用于非常严格的范围内的主机。对于更高级的软件开发人员来说,另一个问题是,由于他们要仿真大量的处理器内部结构,所以速度会非常慢。最后,这些仿真器可能不如真实硬件那样精确。由于速度或复杂性的原因,即使是周期精确的仿真器,也可能忽略了缓存或 IO 的仿真,从而导致有偏差的结果。在大多数情况下,这些仿真器都很接近,但也存在这样的事实,即一个仿真器在某个时候只仿真硬件,因而其行为可能会有所偏差。
这里讨论的仿真器都不是周期精确的。实际上,它们甚至还不完全是行为精确(behavior-accurate)的。(如果出现行为不精确的现象,则称之为一个 bug,这种 bug 通常最终都会得以解决。)
回页首
仿真的用户模式对于好奇的开发人员来说,一个非常方便的特性就是用户模式仿真。如果一个仿真器只仿真处理器和 IO(例如一个网络设备),那么首先需要启动(和仿真)一个 Linux 内核,然后在此基础上启动一个仿真的应用程序。对于更重大的工作而言,这当然很重要,但是对于简单的实验,这样做要方便得多,可以避免完全处理内核。如果仿真器不仅能够仿真处理器,而且还可以仿真操作系统内核,那么对于那些不依赖很多内核服务的小程序,例如只需要使用 write 和 exit 系统调用的小程序,运行起来就要容易得多。
当仿真器正常地碰到一个 PowerPC 系统调用指令时,它便将指令地址存入到 SRR0 寄存器,设置 SRR1 中某些体系结构定义的位,并将控制权转交给物理地址 0xC00,从而仿真这个异常(有些 PowerPC 的变种允许对这种行为有更多的控制,但是这里的这种是传统的 PowerPC 模型)。就像在硬件上一样,仿真的内核在 0xC00 处有它自己的系统调用异常处理程序,因此内核可获得对处理器的控制。
另一方面,当一个支持用户模式仿真的仿真器碰到一个系统调用指令时,它并不是把控制转交给仿真的异常处理程序,相反,它会自己解释该系统调用。最简单的例子是像 read和 write 这样的系统调用:这两种系统调用几乎可以立即转换为仿真器作出的实际的系统调用。用于在仿真的应用程序作出的仿真系统调用与仿真器作出的实际系统调用之间进行转换的 glue 层可能还有其他的功能,例如记录仿真的应用程序作出的所有系统调用。
除了绕开构建一个用于仿真的内核和一个用于启动到其中的文件系统镜像,以及为 IO 配置虚拟网络设备的复杂性之外,这条捷径还加快了仿真的速度,因为绕开了从异常处理程序到 VFS 再到设备驱动程序的很多内核指令,而这些内核指令本来就是用来处理系统调用的。不过,很显然,不在仿真器里运行内核就意味着总体行为可能会有很大的偏差。在最坏的情况下,仿真器系统调用 glue 中出现一个 bug 就会使仿真的应用程序看上去像是充满了 bug,即使它在实际的内核上能够运行得很好也无济于事。
即时编译通过即时编译(Just In Time,JIT)这种方法,可以动态地将解释的字节码(例如,一个仿真的指令流)翻译成本地指令。这种方法不是简单地依次解释和仿真每条指令,而是将整个指令序列转换成本地对应的指令序列,并将它们缓存起来,这样,在随后执行指令序列时,就不需要再进行翻译了。相应地,已解释代码的严格 CPU 限制循环应该可以以接近本地的速度执行,因为本地代码被保存在缓存中。另一方面,带有很少循环的代码的性能则得不到太多的改善。JIT 编译器对于 Java™ 虚拟机来说非常常见,这种编译器也可以用于提高仿真虚拟机的效率。

QemuQemu 是非常新的一种仿真器,它使用动态翻译(例如 Java Just In Time (JIT) 编译器),以便获得良好的性能。在这种情况下,良好的性能大约要比本地硬件慢 4 到 10 倍,这取决于基准的不同。Qemu 支持一些不同的主机和目标,但我们只需考虑 x86 主机和 PowerPC 目标,这正好是受支持的配置。Qemu 还支持远程 GDB (GNUDebugger)连接,这对于调试非常有价值。不幸的是,qemu 不支持用户模式仿真中的 GDB 连接,只支持全系统模式中的 GDB 连接。Qemu 不支持 AltiVec™ 向量处理指令。
PearPCPearPC 是另一种新的仿真器,它可以使用 JIT 动态翻译,但只能和 PowerPC 目标一起在 x86 主机上使用 —— 不过,本文使用的正好是这种环境。它的性能不如 qemu 好,大约要比主机系统慢 15 倍。不幸的是,PearPC 不支持用户环境,因此还需要一个内核和一个基本文件系统(Linux、Darwin 和 Mac OS X 目前都受支持)。PearPC 不支持 GDB 连接,也不支持 AltiVec 向量处理指令(不过开发人员打算在未来版本中添加对它们的支持)。
PSIMPSIM (PowerPC 仿真器)是 PowerPC 仿真的祖宗:它编写于 1994 年,对于当时还算新的 PowerPC 体系结构,它在 Linux 和 NetBSD 的初始端口中起辅助作用。PSIM 与 GDB 代码集成在一起,令人惊奇的是,虽然自 1996 年以后就没有人对它作进一步开发,但它至今仍被使用。通过与 GDB 集成,PSIM 还支持 GDB 连接,包括用户模式。因为早于 AltiVec 出现,所以 PSIM 不支持AltiVec 向量处理指令。
回页首
选择仿真器由于上述原因,本文将使用 qemu。一些常见的基本问题也适用于其他仿真器,但就本文来说,qemu 是最易于构建的。请下载和解压 qemu tarball (请参阅 参考资料),然后:
清单 1:  构建 qemu$ ./configure --target-list=ppc-user$ make
这将产生 ./ppc-user/qemu-ppc,以后还要使用这个文件执行 PowerPC 二进制文件。
回页首
交叉编译在交叉开发中,第二个关键组成部分是交叉编译器(cross-compiler)。交叉编译器运行在一种体系结构上,但是却产生用于另一种体系结构的二进制文件。如果像大多数嵌入式系统开发那样,部署系统相对于开发系统来说存在明显的不足,那么交叉编译可能带来很大的方便。交叉编译器不会覆盖系统的本地编译器,也不会通过任何方式与本地编译器交互。
Crosstool构建 GNU 交叉编译器可能非常容易,取决于牵涉到的体系结构,但有时候也会出现构建中断(build break)的情况。有时可能还要求完成几个阶段的构建,以便让所有组件以正确的方式为彼此进行构建。为了消除猜测,并自动化这个过程,Dan Kegel 开发了一个非常有用的构建脚本,即 crosstool。
请下载和解压最新版本的 crosstool (请参阅 参考资料)。然后:
清单 2:构建 crosstool$ sudo mkdir /opt/cross$ sudo chown $USER /opt/cross$ sh demo-ppc750.sh
这要运行一段时间,在完成时,用于交叉编译的 binutils、GCC 和 glibc 将安装在 /opt/crosstool。请查看一下那里的目录结构,并考虑将它添加到 PATH 环境变量中,这样以后便可以不用输入目录。
继承事业,薪火相传
返回列表