何 巍,贺 飞,顾 明
(1.清华大学 软件学院,北京100084;2.清华信息科学与技术国家实验室 (筹),北京100084;3.清华大学 计算机科学与技术系,北京100084;4.信息系统安全教育部重点实验室,北京100084)
全系统仿真是指在宿主机的操作系统上运行一个模拟硬件的程序来仿真目标硬件,在其上可以运行真实的软件栈(包括操作系统和应用程序)[1]。现有的仿真工具[2-7]大多是基于单机架构的,在使用上,它们往往存在着配置复杂、对机器CPU、内存等配置要求较高,软/硬件逻辑改动无法同步等不足。为此,我们考虑到了嵌入式系统软/硬件开发人员位于分布式环境的特点,设计了一种基于B/S架构的分布式仿真平台。分布式的架构解决了上述单机架构的不足,此外它通过开发人员对子模块软硬件逻辑的局部更新,实现对系统全局仿真模型自动和增量式的更新。我们基于开源嵌入式全系统仿真工具SimSoC[2,3]实现了该仿真平台。同时,我们的工程经验也阐释了将单机架构的仿真工具改成了分布式架构的一般规律。本文首先讨论了嵌入式仿真工具从单机架构改成分布式带来的优点,然后给出了一种基于B/S架构的仿真平台的设计与实现。最后通过两个典型的场景说明了该平台的应用。
在这一部分,我们将比较分布式的仿真平台和基于单机的仿真平台,讨论分布式架构可能带来的优点。
考虑这样一种基于B/S架构的分布式仿真平台:软硬件开发人员位于分布式的环境中,即在不同的地方,使用不同的机器进行开发工作。开发者通过仿真平台的Browser端提交软硬件逻辑、提交仿真任务。仿真平台的Server端负责构建仿真模型,进行仿真计算。如图1所示。
图1 仿真平台的B/S模型
这样的一种分布式架构,相比基于单机的仿真工具,会带来一些显而易见的好处:①无需在本地搭建仿真环境,通过浏览器即可使用;②将非常消耗计算资源的仿真计算从本地机器转移到了Server端;③开发者对软硬件逻辑的改动通过提交到Server可以及时反映给其他开发者;④对通常是命令行界面、较难上手的仿真工具进行了封装,只将有用的功能通过Browser端的GUI暴露给使用者。
此外,相较于单机架构,分布式架构提供了更方便的构建仿真模型的方法。在使用基于单机架构的仿真工具时,最重要、也是工作量最大的任务是构建仿真模型。仿真模型的构建依赖于对于目标系统(尤其是软硬件逻辑及其接口)有全面和深刻的理解。在一些规模较大、逻辑较为复杂的嵌入式开发项目中,单人很难做到理解整个系统。因此,在实际项目开发中,往往有专职的仿真人员。这些仿真人员独立于软硬件开发人员,他们需要在整个开发周期里同开发人员沟通,了解开发人员的开发进度,跟进并理解最新的软硬件代码,搭建/更新仿真模型,将仿真结果反馈给开发人员。仿真人员的存在毫无疑问会增加人力成本和沟通成本。
而在分布式的环境中,在待仿真系统完成了软硬件功能划分后,首先通过Browser端描述系统的架构。此后,在整个系统开发周期中,不同分工的软/硬件开发者只需通过Browser端提交子模块相应的代码逻辑,进行局部仿真模型的构建与更新,与此同时,整个系统的仿真模型会进行自动地、增量式的更新。在嵌入式系统开发中,不同开发者的分工不同。除了软硬件开发者之间的分工差异之外,不同的硬件开发人员可能分别负责FPGA硬件逻辑中不同的子模块。在分布式的架构中,不要求有专人对整个系统有全局性的理解以构建仿真模型,每个人可以专注于他所负责的子模块。
这个仿真平台的重点是分布式框架,而非工具本身。因此在实现上,我们希望尽可能地集成现有的仿真工具,而非重新开发一个仿真工具。
我们选择将开源全系统仿真工具SimSoC变成从单机架构变成分布式。本节将讨论仿真平台实现的技术细节。
在实现上,我们决定使用Flex[8]来开发Browser端。Flex是Adobe公司发布的基于Flash平台开发富互联网应用(rich internet applications,RIA)的技术。Browser端允许用户通过GUI界面完成以下操作:编辑系统架构;提交/更新软硬件逻辑;提交仿真任务,查看仿真结果。
Server端则起到了信息和控制中枢的作用。它负责:维护仿真资源库;运行用户提交的仿真任务;管理提交的软硬件逻辑。
我们使用Java Servlet[9]作为Browser端与Server端的中间层。Servlet由Tomcat服务器进行加载,运行在Server端,它可以将来自于Browser端的Http请求翻译成对Sim-Soc的调用命令。Java Servlet的存在,使得Browser端同Server端的仿真工具解耦,当使用非SimSoC的其它仿真工具时,只需要在Java Servlet重新定义Http请求到仿真工具调用命令的映射即可。
用户在使用Browser端配置系统架构时,并不需要从零开始搭建,而是可以复用一些预定义的仿真资源模块。由于我们使用SimSoC作为Server端的仿真工具,因此这些仿真模块会分别对应SimSoC里一些已经实现的SystemC类。这些预定义的仿真模块包括各种体系架构的嵌入式处理器,内存、总线、各种外设等。当Browser端初始化时,会从Server获得一个描述仿真资源库的XML文件。在该文件里,定义了各个模块的描述、端口、属性等信息。
我们也可以参照SimSoC里现有的类来实现一个自定义模块,然后修改SimSoC的Makefile文件,将自定义模块加入其中后对SimSoC重新编译,并且在描述资源库的XML文件里添加对自定义模块的描述。
为了方便用户快速地描述系统架构,我们在Browser端提供了系统架构图的画图工具。用户可以通过画图工具以拖拽的方式将资源库里模块加入到系统架构中,并使用连接符来指定各个模块的连接关系。图2是配置系统架构图时的用户界面,它描述了ARM CPU、Memory和总线3个模块,其中CPU和Memory的rw_port端口与总线的target_sockets端口相连,代表着将CPU和Memory绑定在了总线上。
此外,我们需要为每一个加入系统架构的模块指定属性。有些属性是固定值。而有些属性需要用户指定,比如Memory模块的大小或者CPU是大端或者小端。为此,我们为每一个模块关联了一个key-value链表。链表里的每一对key-value指定了该模块需要在运行时确定的属性。在Browser端选择某模块时,可以通过GUI界面来编辑模块属性。
图2 架构:CPU和Memory绑定到Bus上
当在GUI界面完成对系统架构的描述后,我们需要将架构以XML的格式上传并保存在Server端。描述系统架构主 要 有 纯 文 本[5]和 XML[4,6,7]两 种 方 式。 之 所 以 选 择XML而非纯文本格式,是因为XML在描述不可预见的数据和属性时更具有优势。比如,资源库里某模块新添加了需要在运行时确定的属性时,不需要更改XML格式的Schema,即可为XML增加描述该新属性的能力。某个模块对应的XML结点component主要有两个子结点组成,第一个子结点nodes描述了架构中各模块的类型、ID及其各自属性,第二个子结点connections描述了各模块端口之间的连接关系。图3是图2中架构对应的XML描述。
图3 图2所示的架构对应的XML描述
当需要调用某具体的仿真工具进行仿真时,需要将XML格式的系统描述转变成该工具可以识别的架构描述。对于SimSoC,架构是通过SystemC[10]顶层模块来描述的。在该SystemC顶层模块中,实例化了需要用到的SystemC子模块,并且指定了子模块间的端口[11]连接关系。
图4是图2中架构最终生成的SystemC顶层模块。
图4 图2中架构生成的SytemC顶层模块
从XML描述生成顶层模块的SystemC文件是由Java Servlet完成的。在代码生成开始时,首先要进行预处理,即为资源库里各仿真模块获得它的头文件路径、类名等信息,以便接下来的代码生成时用到。然后解析描述架构的XML文件,获取所用到的模块及各模块之间的连接关系。接下来,依次生成include语句,常量定义,为顶层类生成成员变量声明、构造函数和析构函数。在生成构造函数时,需要实例化各模块对应的成员变量,以及绑定各变量相应的端口。
Server端使用SimSoC作为仿真工具,由于SimSoC实现了全系统仿真,可以运行同真实情况下一致的软件栈,所以软件开发人员只需要提交真实开发中用到的编译好的二进制文件即可:比如对于ARM嵌入式处理器,只需要提交通过ARM交叉编译工具生成的elf文件。软件开发人员并不需要为仿真平台准备单独的编译工具。
硬件人员提交 “运行”在FPGA上的硬件逻辑相对复杂。因为仿真平台不包含真实的FPGA,所以硬件开发人员并不能直接提交烧录到FPGA上的二进制文件。受Giano[4]做法的启发,我们设计了一种基于 “双向内存抽象”的描述FPGA硬件逻辑的方式。它要求FPGA被看作满足特定接口要求的 “总线从”:FPGA在虚拟总线上占据了一段内存,这是一段CPU和FPGA均能访问的双向内存。CPU可以向这段内存写入控制信号,控制FPGA的逻辑。CPU也可以从这段内存中读取数据,获得FPGA的内部状态。
这里的双向内存的含义是抽象的,仿真平台并不对这段双向内存的具体位置或语义做任何预先的假定,它可以是实际中的设备寄存器文件,先进先出队列(first-in firstout,FIFO),通 用 输 入 输 出 (general-purpose I/O,GPIO),或者是异步总线上的传输数据包。这样的一种抽象可以在最大限度上描述FPGA和CPU之间的基于不同形式的交互。虽然SimSoC本身并不能仿真FPGA可编程硬件逻辑。但是在 “双向内存抽象”的假设下,我们可以将FPGA视做虚拟总线上一个SystemC模块。它在虚拟总线上占据了特定的地址,并且会对该段地址的读写操作做出相应的反应。这一点上,FPGA同其它连接到总线上的内存映射外设模块没有区别。
硬件开发人员在提交硬件逻辑时,需要将项目开发中的用到的Verilog/VHDL等HDL源码,抽象出行为模型,改写成SystemC代码。在Browser端,提供了新建、修改、删除SystemC文件的功能。需要说明的是,可以有任意多的SystemC文件负责描述FPGA硬件逻辑。不同的SystemC文件对应于FPGA中的某个功能子模块。这样,不同分工的硬件开发人员只负责提交、更新其负责的子模块对应的SystemC文件即可。唯一的要求是所有SystemC文件编译生成的目标文件满足与总线的 “双向内存抽象”的假定,即可以响应来自总线的读写操作。
下面我们通过图5中的例子说明了如何将Verilog代码改写成SystemC代码。图5中test模块的verilog代码中,mem是test与CPU共享的一段4*32字节内存。CPU向REG_A写入值为0或者1的控制信号,在每个时钟周期的上升沿,test模块会根据REG_A值进行相应的操作。将test模块改写成图5中的FPGA类后,该SystemC类通过rw_socket与总线相连,需要实现的接口是read_word和write_word两个方法,分别处理来自虚拟总线的读写操作。在read_word和write_word方法中,当CPU读REG_A傎或向REG_A写入傎时,会触发相应的逻辑。
图5 test模块的Verilog代码和对应的SystemC代码
本节将通过示例来说明这个仿真平台的使用。第一个例子仿真在AT91X40系列板卡上运行eCos[12]操作系统,通过这个例子,我们将展示该仿真平台的基本应用场景。第二个例子仿真一个32位计数器,通过这个例子,我们将展示如何在仿真平台上进行软硬件逻辑的协同开发。
AT91X40系列属于Atmel公司AT91 16/32位微控制器家族。它包括ARM7TDMI处理器核,内存(包括RAM、ROM和FLASH)和外设等。其中,ARM处理器核,内存等对应的仿真模块在SimSoC标准发行包里已提供。另外,我们参考AT91X40的官方文档为它的一些外设如中断控制器、通用异步收发传输 器(universal asynchronous receiver/transmitter,UART)、计时器等用SystemC代码编写了相应的仿真模块,并且编译到了SimSoC中。
通过仿真平台的Browser端,新建一个工程,将需要用到的仿真模块如ARM Processor、Memory、各种外设及虚拟总线以拖拽的方式添加到系统架构图中,并指定模块间的连接关系。Browser端的系统架构如图6所示。
图6 AT91X40微控制器架构
我们根据AT91X40的官方文档,为各个模块设置相应的属性,比如分别设置3个Memory模块的起始地址和大小,使之分别模拟RAM、ROM和FLASH。我们还需要将编译好的eCos操作系统elf格式的启动引导文件通过ARM Processor的属性窗口上传到Server上。接下来,我们在Browser端选择编译该工程,在编译期间,Server端的Java Servlet会根据描述架构的XML生成SystemC顶层模块代码,并且将其编译成可执行文件topExecutable。编译完成后,通过Browser端提交仿真任务,Java Servlet会调用SHELL命令“./topExecutable pathOfElfFile”,开始运行topExecutable,并且将eCos启动引导文件的路径pathOfElfFile作为参数传递给它。
我们可以将运行时USART仿真模块的I/O通过Socket重定向到Browser端。图7是Browser端显示运行eCos时USART串口输出的界面,用户也可以通过该界面向USART输入指令。
图7 通过browser端查看AT91X40的USART输出
该示例来自于Giano[4],它是一个32位计数器,系统架构如图8所示。
图8 32位计数器架构
在该示例中,我们将ARM处理器、内存和FPGA连接在虚拟总线上。此外,我们还在总线上连接了一个名为Debug Console的调试信息输出控制台。计数器控制逻辑和计数逻辑分别实现在FPGA和ARM上。ARM和FPGA上的C与Verilog核心代码如图9所示。
可以从图9中的Verilog代码看到,ARM处理器和FPGA之间共享了一段4*32字节的内存。ARM通过向Counter_Reset写入Reset信号重置计数器的计数,通过向Counter_State写入Enabled信号通知计数器开始计数,通过向Counter_Reset写入Reset信号重置计数器的傎,通过读取Counter_Value得到计数器当前的计数值。显然,这样的ARM和FPGA交互的方式符合上文中 “双向内存抽象”的假定。
C代码的main函数的主要逻辑是,在开始时读取计数器的值,向Counter_State写入Enabled让计数器开始计数,然后执行一个空循环,消耗若干个CPU周期,最后再读取当前计数器的计数,并且把计数器计数的增加在Debug Console中打印出来。
在Browser端的GUI界面双击FPGA结点,进入FPGA硬件逻辑编辑界面,新建SystemC代码文件,将图9中的Verilog代码改写成SystemC代码。编译并运行该工程后,可以在browser端查看Debug Console的输出,如图10所示,可以看出在ARM中C代码执行spin函数时,FPGA中的计数器计数值增长了1999365。
图9 计数器的ARM上的C代码和FPGA上的Verilog代码
图10 Debug Console的输出
为了更好地辅助嵌入式系统软硬件协同开发,本文设计了一种基于B/S架构的分布式协同仿真平台。该平台适应了嵌入式系统开发人员位于分布式环境中的特点,在开发者各自更新子模块逻辑的前提下,系统全局仿真模型可以实现自动、增量式的更新。开发者在Browser端通过GUI界面配置系统架构,提交软硬件逻辑,而将仿真计算、管理仿真资源库等功能转移到了Server端,这样的一种Browser端和Server端各有分工的架构,弥补了基于单机架构的仿真平台在使用上的缺点。我们基于开源嵌入式全系统仿真工具SimSoC实现了该仿真平台,并应用了两个实例来说明该仿真平台的使用。
未来的工作包括进行更多的案例研究以提高平台的可用性。此外,我们将研究该仿真平台中,如何对提交的软硬件逻辑进行版本管理,以帮助快速定位带来系统功能错误的代码逻辑变动。
[1]KE Huacheng.Design and implementation of a framework of complete machine simulator for embedded systems [D].Hangzhou:Zhejiang University,2009:9-10 (in Chinese). [柯化成.嵌入式全系统模拟器框架设计与实现 [D].杭州:浙江大学,2009:9-10.]
[2]Helmstetter V Joloboff.A SystemC TLM integrated ISS for full system simulation [C]//Macao,China:Proceedings of the IEEE Asia Pacific Conference on Circuits and Systems,2008:1759-1762.
[3]Helmstetter C,Joloboff V,Xiao H.SimSoC:A full system simulation software for embedded systems [C]//Guiyang,China:International Workshop on Open-source Software for Scientific Computation,2009.
[4]Forin A,Neekzad B,Lynch N L.Giano:The two headed system simulator [R].Technical Report MSR-TR-2006-130.Redmond,WA,US.Microsoft Research,2006.
[5]Skyeye Community.Skyeye user manual[EB/OL].[2011-01-26].http://skyeye.org.
[6]Beltrame G,Bolchini C,Fossati L,et al.ReSP:A nonintrusive transaction-level reflective MPSoC simulation platform for design space exploration [C]//ASP-DAC.Seoual:Korea,2008.
[7]Beltrame G,Fossati L,Sciuto D.High-level modeling and exploration of reconfigurable MPSoCs [C]//NASA/ESA Conference on Adaptive Hardware and Systems.Noordwijk:Nethe-rlands,2008.
[8]Adobe Flex Community.Using adobe flex 4.5 [EB/OL].[2011-04-29 ]. http://www.adobe.com/devnet/flex/documentation.html.
[9]Rajiv Mordani.Java Servlet specification V3.0 [EB/OL].[2010-12-01].http://jcp.org/en/jsr/detail?id=315.
[10]Open SystemC initiative(OSCI).IEEE standard for standard SystemC language reference manual(IEEE Std 1666-2011)[S].http://standards.ieee.org/getieee/1666/download/1666-2011.pdf,2012-01-09.
[11]Open SystemC Initiative(OSCI).OSCI SystemC TLM 2.0,Draft 2for public review [EB/OL].[2008-06-06].http://www.systemc.org/.
[12]JIANG Juping.Development and application of embedded configurable operating system,eCos [M].2nd ed.China Machine Press,2008 (in Chinese). [蒋句平.嵌入式可配置实时操作系统eCos开发与应用 [M].2版.北京:机械工业出版社,2008]