(上海交通大学 软件学院,上海 200240)
随着大数据分析和人工智能计算的大规模普及和应用,数据中心的计算负载规模在不断扩大[1].在此背景下,将计算任务进行分门别类,并根据不同任务的特点用专门设计的加速处理芯片取代通用处理器,从长远来看成为了一种可行的降低成本或提高服务质量的方式.其中,为人工智能计算特化的通用图形处理器(generalpurposed GPU)和应用专用处理芯片(application-specific integrated circuit)TPU[2]已在该领域取得了显著成就.但是,数据中心中除了日益庞大的人工智能应用以外,其他配套的支持计算也在相应地扩大规模,这些方面包括数据库处理、网络通信、虚拟化支持等等.利用专用芯片来加速这些基础计算同样能够获得可观的收益.使用硬件加速的理想情况是能够直接部署经过专门优化的ASIC,但是ASIC 的设计、生产和部署都需要较长的周期和较高的成本,在需要快速迭代的生产环境中或者在需要控制风险的情况下,FPGA 作为一种可重新配置的硬件平台成为了一种首选的过渡或者替代方案[3,4].
FPGA 除了能够在生产环境中作为专用芯片的一种过渡和替代,其优秀的可定制性和可重配特点使得它在异构计算的学术研究中也发挥着重要作用.与ASIC 相比,基于FPGA 的研究主要有两点优势:(1)区别于ASIC的静态功能,FPGA 可重配特点引入的功能动态变化在系统设计时可创造更多可能;(2)区别于ASIC 的流片流程,FPGA 的快速烧写在降低成本的同时也使应用可以快速地在硬件上验证并测试端到端的性能.这些特性近年来吸引了许多学者投入到FPGA 的应用研究中,这些研究从最初的加速器设计慢慢扩展到了与FPGA 相关的操作系统、体系结构等领域[5-7].
然而,面向FPGA 硬件平台的硬件设计和系统集成方法相较于软件工程和程序语言领域的发展仍然处于十分初级的阶段.目前主流的FPGA 加速系统的开发可以大致分为3 个部分:硬件加速器设计、软件驱动设计和软件应用集成.硬件设计虽然经过了多年的发展,已经形成了成熟的工具链,但是这些初衷是辅助专业硬件工程师的开发工具和语言,只提供十分底层的硬件抽象,例如寄存器和逻辑运算.由于缺少指令集抽象,软件工程师无法直接将算法映射成硬件逻辑,对于希望快速验证硬件加速有效性的云应用开发者和实验室学者等非专业的硬件工程师来说,硬件设计无疑成为了利用FPGA 加速上门槛最高的一关.而第2 个难点是需要实现驱动软件来构建底层软件和硬件的数据通信机制并提供接口给上层应用.这一部分工作即使利用现成驱动(例如PCIe DMA 驱动),也需要对底层通信协议有充足的了解,并从软硬件两方面进行适配:软件需要对数据进行序列化和反序列化,硬件则需要构建加速器的外围数据通信模块.相比于硬件加速核心的设计和验证,这一步的工程量并不逊色甚至犹有过之.克服前两点之后,需要经过最后一步的软硬件集成,开发者才能真正地对应用进行加速.此时,可能由于前期软硬件工程师协同的沟通失误或者考虑欠缺,应用的性能受限于不理想的硬件抽象和接口,如果需要改进,则往往需要重新进行各部分的设计.
FPGA 的高度可定制化给其带来灵活性的同时也引入了功能是由硬件来实现还是由软件来实现的权衡,如果开发框架不能提供清晰的软硬件交互界面和设计空间定义,加速系统容易陷入软硬件功能过于耦合,调试、迭代、维护十分困难的境地.为了提高FPGA 加速系统的开发效率,研究者们在一脉相承的传统硬件开发工具的基础上提出了许多针对FPGA 特点的优化框架以应对不同的需求.从传统的硬件描述语言开始,这些新的框架和工具主要走上了两条道路:高层次综合(high-level synthesis)和高级语言实现的硬件描述语言(high-level hardware description language).近年来,国内外FPGA 应用的研究已颇具规模[8,9],但是与国外传承有序的EDA(electronic design automation)研究历史相比,国内关于硬件开发工具与框架的研究资料依然十分缺乏,本文旨在对FPGA 相关开发工具的发展状况作一定的总结和归纳,以提供从事本领域工作的国内学者参考.接下来,本文第1 节将综述近年来硬件及硬件加速系统开发工具相关的典型工作.第2 节介绍我们自己针对FPGA 加速系统开发框架的设计实践.第3 节总结全文并提出未来展望.
本节介绍一般硬件以及硬件加速系统开发工具和框架的相关工作,并引入硬件加速系统设计的基本概念和背景知识.
广义来说,后文将提到的高层次综合工具使用的描述语言和高级硬件描述语言都可以归纳进硬件描述语言,本节讨论狭义的硬件描述语言,即以VHDL 和Verilog 为代表的经典硬件描述语言.
硬件描述语言的出现可以追溯到20 世纪60 年代,在1971 年,Bell 和Newell 在他们的教材中提出了影响至今的硬件设计抽象级别——寄存器传输级(register-transfer level)[10].基于该抽象,DEC(digital equipment corporation)首先设计了ISP 语言以及其扩展版本来描述PDP-8 和PDP-16 机器中的基于寄存器传输模块(register-tansfer module)的硬件行为[11].1979 年左右,Kaiserslautern 大学的团队开发出KARL,以支持结构化硬件设计和日益兴起的超大规模集成电路(VLSI)设计[12].同一时间,为了满足同样兴起的可编程逻辑设备(PLD)的商业化需求,DATA I/O 公司也开始设计面向可编程设备的ABEL[13].
随着超大规模集成设计的逐渐流行和现场可编程设备的发明,传统的基于电路图绘制(schematic capture)的硬件设计方法受限于适合处理的晶体管数量(通常是数百个)已经越来越难以满足设计效率的需求,在20 世纪80 年代,Verilog[14]和VHDL[15]相继出现并逐步发展成熟.VHDL 和Verilog 的设计初衷都是希望用开发者熟悉的编程语言来描述设计好的电路图以便于工程管理,其中,VHDL 借鉴了大量Ada 语言的理念和语法而Verilog 借鉴了C 语言的编程风格.相比于其他硬件描述语言,类Ada 和类C 的语言风格更受开发者青睐,随着VHDL 和Verilog 的标准化,两者逐渐取代了其他硬件描述语言,并统治硬件设计领域直到今日.初期版本的VHDL 与Verilog 的功能十分类似,相比于Verilog,VHDL 有更高的抽象级别和语言复杂度.但是进入21 世纪以后,经过Verilog 到System Verilog 的演变,以及多次迭代(最新标准是IEEE Standard 2017[16]),Verilog 引入了许多提高生产效率的语言特性,并提高了对大规模系统的描述能力.在此基础上,又由于C 语言使用的广泛性,Verilog相比于VHDL 得到了更大规模的应用.
基于寄存器传输级抽象的硬件描述语言在逻辑综合技术引入之前并不足以完成完整的硬件设计,VHDL 和Verilog 初期都只是用于描述和归档门电路级的电路图设计.类比高级软件编程语言,逻辑综合器可以被理解为编译器,即将寄存器传输级的设计翻译成门电路级的网表设计.但是,与高级语言的编译器不同,由于没有统一的指令集抽象和确定的硬件布局,描述门电路连接方式的网表还不足以在硬件上“运行”.以一般FPGA 开发为例,我们还需要经过“布局布线(place and route)”步骤,将网表与特定硬件平台上的物理资源对应起来,并通过下载配置才能让FPGA 实现我们想要的功能(放置与路由由于涉及到FPGA 硬件的技术细节,目前这一步一般都是由FPGA 生产商提供的闭源软件来实现,因此本文讨论的开发框架将不包括这部分内容).系统设计的经验告诉我们,提高抽象层次的代价往往是性能的损失,而这一点在硬件设计上也没有例外.基于硬件描述语言和逻辑综合器生成的设计相比于有经验的工程师使用传统方法完成设计,几乎总是需要更大的硬件面积和提供更低的性能.然而,随着硬件集成度的提高,硬件本身性能的发展和综合算法的改进,人们在设计质量和开发效率的权衡中,最终接受了高开发效率的工具,这一点与软件编程语言的发展是类似的.
硬件描述语言经过30 多年的使用,尽管自身依然在迭代和发展,但面对新的需求和挑战,它们遇到了与自己前身类似的问题:语言本身的局限性限制了开发效率的进步.近年来,FPGA 应用研究的兴起吸引了大量非专业硬件背景的工程师投入到FPGA 的开发中,不同于C、Ada 等底层语言,他们熟悉的工作语言经常是C++、Java、Scala 等面向对象语言或者是Python、JavaScript 等脚本语言.对于新的使用群体来说,硬件描述语言曾经语言友好的优势不复存在,而另一方面,区别于硬件工程师是从门电路级抽象提升到寄存器传输级抽象,软件工程师却是从算法级抽象降低到有明显语义鸿沟的寄存器器传输级抽象,抽象级别的优势也不复存在.而对于传统的硬件工程师来说,现代软件编程框架中体现的软件工程理念同样吸引着他们,经典硬件描述语言中缺乏面向对象编程、参数化类型、类型安全等特性,这在项目规模不断扩大的今天,已经开始给项目管理、维护带来巨大的困扰.为了适应新的需求,在经典硬件描述语言的基础上,新的硬件开发工具或框架基本走上了两条道路:高层次综合[17-19]和高级硬件描述语言[20].并形成了如图1 所示的层次,接下来,我们将依次介绍高层次综合和高级硬件描述语言.
Fig.1 The hierarchy of development tools图1 开发工具层次
事实上,在基于寄存器传输级抽象的硬件描述语言流行之前,学术界就已经开始将算法级或行为级描述映射成硬件设计的研究.这些早期的工作大约从20 世纪70 年代一直持续到90 年代初,在这段时间涌现了许多论文和学术著作.其中影响力较大的是Paulin 与Knight[21]、Camposano 与Wolf[22]、Gajski[23]以及De Micheli[24]等学者的工作.这些工作探索了高层次综合的基本原理和概念,并发展出了高层次综合工具的基本特征:专用输入语言加编译综合器.尽管对后世影响深远,但这些工作的商业化尝试都失败了.在当时的背景下,硬件工程师才刚开始接受寄存器传输级的设计抽象,硬件描述语言都还没有普及,高层次综合技术的引入过于超前,具体来说该技术当时存在以下两个问题.
(1)较高的学习成本和较低的硬件设计质量.高层次综合工具一般使用特别定义的行为或算法描述语言作为输入语言,这些语言与硬件描述语言相比更加专用化且没有通用的规范,基本依赖于厂商的支持.同时在可以预计的硬件设计质量下降的前提下,开发人员没有学习的欲望;
(2)适用的专用领域没有广泛的市场.当时的许多高层次综合工具是为数字信号处理器(DSP)的开发设计的,这类设计的特点是专注数据流和结构化的硬件,这在大部分开发人员都在进行非结构化随机逻辑集成这类侧重控制流设计的背景下是不合时宜的.
虽然早期学术成果商业化的尝试失败了,但是高层次综合提升开发效率的前景以及硬件描述语言蓬勃发展的现状依然吸引了Synopsys、Cadence 和Mentor Graphics 这些大型的电子设计自动化(EDA)公司投入到了高层次综合工具的研发中,典型的代表包括Synopsys 公司的Behavioral Compiler[25]、Cadence 公司的Visual Architect 和Mentor Graphics 公司的Monet Tool[26].在20 世纪90 年代中后期,这些产品一度吸引了广泛的关注.这些工具的特点是使用单独设计的行为级描述语言作为输入语言,并直接生成门电路级的设计.不过,这次尝试依然未能取得期待中的成功,并且在之前的基础上又暴露出了有关产品定位的新问题.
(1)错误的目标群体,并以经典硬件描述语言为竞争对手.这时期的商业产品以正在接受寄存器传输级抽象、开始使用VHDL 和Verilog 等硬件描述语言的硬件设计师为目标用户,提出了基于算法或行为级抽象的新语言和相应的综合器.这些工具直接从高抽象级别的描述综合生成门电路级的设计,与硬件描述语言并不兼容且互相替代,这迫使工程师们必须从中做出选择.在已经逐步接受硬件描述语言的背景下,当时的硬件工程师们关心的问题是,新的工具能否用相同的工作量设计出更高质量的硬件、是否可在硬件设计质量不变的情况下减少工作量,以及学习曲线是否陡峭.但是,新的工具不仅学习曲线陡峭,而且几乎不能提高硬件设计质量或者是降低工作量;
(2)没有清晰地界定基于控制流的设计和基于数据流的设计,导致未能发挥出自身优势.控制流设计大多包含随机逻辑和跳转结构,而数据流的设计则大多为结构化硬件.基于寄存器传输级的综合器可同时处理好控制流和数据流的设计.高层次综合由于具有更高的抽象级别,很难生成性能理想的复杂控制流硬件,但是对于更适合抽象和采取针对优化的数据流设计,高层次综合往往能够生成性能更加良好的硬件.但是,这时的高层次综合工具未能专注于自身的特长,反而试图在一个框架上来完成各种类型的硬件设计,以致失去了自身的竞争优势.
时间进入21 世纪,由于超大规模集成电路的持续发展,Verilog 和VHDL 的开发效率处于瓶颈状态,同时,由于异构计算和硬件加速的兴起,大量软件工程师和系统架构师开始尝试协同设计.在软硬件工程师对硬件开发效率应加以提高的共同需求下,之前高层次综合推广的失利未能阻止工业界和学术界的新一轮尝试.与之前的环境相比,在新世纪高层次综合的应用有了以下优势.
(1)大数据处理相关应用的规模不断扩大,针对特定类型应用的硬件设计工具拥有了可观市场,尤其是有关人工智能和数据库的应用.这些应用在规模大的同时更侧重数据流的处理,让高层次综合有了用武之地.
(2)经典硬件描述语言开发效率已经达到瓶颈,开发人员有了对更高层次抽象的明确需求.
在这一阶段,不断有新的高层次综合工具取得学术上和工业上的成功,以至于到了2019 年依然不断有新的工具被提出,其中影响较大的包括Bluespec[27]、LegUp[28,29]、Vivado HLS[30]和Intel HLS Compiler[31].新的工作吸取了之前工作的教训,大多做出了如下改进.
(1)主要使用或支持C、C++等广泛使用的通用编程语言作为输入语言,这使得工具具有了更广泛的受众并降低了学习成本;
(2)以Verilog 或(和)VHDL 为输出结果.这使得新的工具可以兼容老的设计,并在生成的硬件性能不够理想时提供了手动优化的可能,进而丰富了工具的适用场景;
(3)针对特定类型设计的综合算法进行专门优化.以牺牲通用性换取性能,这强化了高层次综合的优势.
到目前为止,我们讨论的工作都是针对单独的硬件设计而言的,对于传统的硬件来说,从硬件设计到硬件应用中间有较长的时间跨度,也会由各自的团队来完成.但是,随着FPGA 的兴起,由于FPGA 可以通过快速的下载配置直接形成对应的硬件,设计和部署的分工变得模糊起来,人们需要集成的框架来快速构建加速应用.而要构建一个完整的加速系统,如本文开始部分中提到的,我们还需要硬件驱动和软件集成.将驱动和集成功能包含在一个框架下,在我们不知道用户会设计出具有什么功能的硬件和使用什么样的接口进行数据传输的情况下是十分困难的.如果设计十分底层的接口来提高适用性和灵活性,则依然会保留较多的工作量给开发者;而如果规定接口协议和做各种假设来提高抽象层次,则会对用户的硬件设计带来各种限制.随着一些高层次综合工具开始有针对性地处理特定应用,以及OpenCL 等计算框架的兴起,工具开发人员不再面对硬件功能未知的情况,而完整的开发框架设计也变得可行.
Xilinx 和Intel 分别推出了SDAccel[32]和Intel SDK for OpenCL[33],用于将OpenCL 定义的加速器部署到FPGA 上,并包含基于PCIe 的主机与FPGA 数据传输机制的集成.对于特定领域的应用,DnnWeaver[34]和Inter OpenVINO[35]可以根据的Caffe,TensorFlow 等机器学习框架定义的模型自动生成硬件并部署到主机+FPGA 的加速平台上,而p4FPGA[36]框架可以将用p4 语言定义的数据面板部署成FPGA 包处理器.这些框架相比于通用的高层次综合工具更受系统架构师和软件工程师的青睐,它们能够显著降低开发门槛,提高开发效率,方便系统的早期验证.虽然这些框架生成的硬件设计与使用硬件描述语言完成的设计相比依然存在不小的性能差距,但是,从硬件描述语言的发展历史来看,对开发效率的需求将推动综合算法的改进.同时,由于硬件本身性能的提高,具有更高生产效率的工具将逐渐取代生产效率低的工具.
对依山而建、受山坡地表径流危害的城镇、集中居民点、重要设施等,需修建截洪沟、排洪沟渠,将坡面地表径流引入沟道排泄。对沟道内淤积的泥沙、乱石、杂物和人为卡口进行清理疏挖,提高沟道泄洪能力。重点在城镇河段清除河道行洪障碍,确保沟道泄洪畅通。疏浚、扩挖的淤积物、弃渣等应堆放在距沟道有一定距离的低洼处,严禁人为设障。截洪沟、排洪渠应尽量利用坡面原有沟埂、天然沟道,其断面大小应满足排洪量的要求。在经过重要位置或弯道凹岸、跌水等冲刷强烈地带,需考虑必要的护砌措施。
高层次综合工具在侧重数据流处理的加速系统中得到了良好应用,但对于控制流复杂的硬件设计,比如非结构化的硬件设计和基于指令集的处理器设计,高层次综合工具往往需要与其他寄存器传输级设计工具相互配合.因此,在对硬件性能要求较高、需要在微架构上进行创新的设计情景下,寄存器传输级设计工具依然在发挥着重要作用.将经典硬件描述语言使用新的高级编程语言重新包装以获得高级语言在编程效率、维护管理上的优势同样吸引了大量研究人员.本文将提供寄存器传输级抽象设计接口,并使用高级编程语言作为输入语言的硬件描述语言归纳为高级硬件描述语言.这些高级硬件描述语言通常会利用高级语言中的面向对象、语法糖等机制来扩展自身的数据结构并提供更便利的语法接口,或者以库的形式来模板化特定类型硬件的设计.在一定程度上,这些方法提供了更高层次的设计抽象,但与高层次综合不同,这种抽象更多的是语言机制上的抽象,它没有隐藏硬件数据传输的底层机制,也就是说,没有沟通软硬件描述的语意鸿沟.因此,我们认为目前大部分高级硬件描述语言依然处在寄存器传输级抽象.表1 根据输入语言的特征列举了2000 年以来典型的高级硬件描述语言.
函数式编程风格一度被认为是最适合进行硬件描述的编程风格,函数式语言作为输入语言的开发工具几乎涵盖了硬件设计的各个抽象层次,其中一部分工作可以被归纳为高层次综合工具,例如Bluespec,还有一部分则可以被归纳为高级硬件描述语言,例如表1.函数式硬件定义的优势十分突出:简明的语义结构便于形式化验证和对递归的良好支持.这些优点使得函数式硬件描述语言在定义大规模组合逻辑电路时十分简练而易于测试验证.但在定义时序电路时,函数式定义却不够直观,以至于各种工具都采取了不同的定义方式,而未能达成一致的抽象.由于缺乏标准、学习成本过高、函数式编程在应用开发上使用较少等因素,在当前背景下,函数式硬件描述语言大多成为了学术语言,较少在实际生产中得到应用.
Table 1 High-level HDLs classified by characteristics of input languages表1 根据输入语言特征分类的高级硬件描述语言
另一方面,基于Java、Python、Scala 等支持面向对象编程和命令式语言风格的高级语言构建的硬件描述语言,由于本身语言更优秀的生态和更广泛的用户基础,近年来我们能观察到它们持续的更新和更深入的发展.尤其是内嵌于高级语言的硬件描述语言,因为继承了原本语言的语法,有更低的学习成本,吸引了更多开发者的使用.其中,基于Scala 语言的Chisel[42],已经形成了硬件定义与测试一体的集成框架,并通过对RISC-V 设计的特化支持,扩展了自身的硬件设计生态.
高级硬件描述语言总体来看目前还处于学术研究阶段.一方面是因为这些语言还没有形成充实的生态基础,无法吸引硬件工程师付出学习成本进行转型;另一方面则是因为这些语言并不专注于通过提供特定的抽象和完整的集成框架来降低开发门槛,因而对软件工程师也未能构成足够的吸引力.如图2 所示,与高层次综合工具正在向应用层特化发展,逐步让底层硬件细节透明化不同,高级硬件描述语言则在确定了寄存器传输级抽象的基础上,在扩充自身的硬件实现生态,以形成类似于C++、Java 等语言的丰富库支持.本文接下来将介绍我们在高级硬件描述语言方向上的实践工作,其中包括高级硬件描述语言ScalaHDL 以及基于ScalaHDL 的系统集成开发框架VeriScala[47].
Fig.2 The prospectives of high-level HDL and HLS图2 高层级硬件描述语言和高层次综合的发展方向
在硬件加速的学术研究中,我们经常需要进行微架构的创新,常常遇到Verilog 或VHDL 学习门槛高、开发周期长、软硬件集成麻烦的困难.Xilinx 推出了Vitis 工具,通过将高层次综合和细粒度的RTL 优化集成在一个框架下来满足这样的需求.但是,Vitis 虽然提供了统一的框架,开发者依然需要使用多种语言来进行综合开发,这显然增加了工程维护和管理的难度.在高层次硬件描述语言的基础上构建集成开发框架可以很好地解决这个问题.本节将介绍我们实现的ScalaHDL 硬件描述语言,以及基于ScalaHDL 的开发框架VeriScala.
基于高级硬件描述语言的加速系统开发框架设计目标主要有两方面:充分利用高级语言在开发效率、维护管理上的优势并提供简便的集成策略.具体来说有以下几点.
(1)支持丰富的代码重用机制.需要提供包括继承、参数化类型在内的特性来提高代码重用率和可维护性.
(2)易用的语言抽象.一方面需要通过支持面向对象编程来进行模块化设计,另一方面还需要将RTL 抽象在高级语言中进行映射以帮助软件工程师的理解.
(3)支持库的设计和使用.需要通过支持硬件设计的引用方法,让现有设计逐渐形成丰富的生态,进一步提高生产效率.
(5)支持快速的系统集成和部署.需要在框架内处理好底层硬件数据传输机制,并提供简洁的软件接口.
理想的设计流程如图3 所示,软硬件设计在同一种高级语言的上下文中,其中,软件计算需求可以通过函数接口来定义,而硬件设计则可以使用内嵌的高级硬件描述语言来定义.框架本身将包含通信相关经过适配的软硬件库,基于基础通信驱动可以实现应用的后端,而基于硬件通信子系统可以实现加速硬件的通信外围.接下来,完成的硬件设计可以直接输入给软件模拟器进行硬件功能测试,而软件模拟器又可以作为模拟硬件与驱动对接,完成整个加速系统的功能测试.最后用验证过的加速硬件替换模拟硬件,从而完成加速系统的集成.
面向上文的设计目标和开发流程,我们设计实现了基于ScalaHDL 语言的VeriScala 框架.后面将在第2.2 节介绍ScalaHDL 语言的设计,在第2.3 节介绍VeriScala 中软硬件协同的集成支持,在第2.4 节展示VeriScala 的代码示例和实验结果.
Fig.3 An ideal workflow图3 理想开发流程
ScalaHDL 是一种内嵌于Scala 语言的硬件描述语言,它被实现成了Scala 库而不需要修改Scala 编译器.选择Scala 作为框架输入语言是因为:(1)Scala 是一种多范式的编程语言,既可以支持面向对象编程,也能良好地支持函数式编程,提供了更多设计的可能性;(2)Scala 基于Java 虚拟机运行,可以受益于丰富的Java 生态.如图4展示了ScalaHDL 的整体框架,主要分成硬件定义和测试两个模块.ScalaHDL 使用高扩展性、可插拔的模块化设计,核心功能实现在HDLBaseClass 和SimulationSuite 两个类中(图中红色模块),分别支持硬件的定义和测试.而扩展的语言特性,例如新的硬件描述语句语义或者更高的设计抽象则可以实现在Scala 的Trait 定义之中(如图4 中浅绿模块所示),只需在硬件定义时指定需要的Trait 即可使用对应的特性.在ScalaHDL 中我们实现了BasicOps 以支持基本的硬件定义功能,以及通过Translator 来完成ScalaHDL 到Verilog 的翻译.
Fig.4 The architecture of ScalaHDL图4 ScalaHDL 架构
ScalaHDL 使用Scala 语言中原生的Class 作为硬件模块定义的容器,通过继承HDLBaseClass 并引用BasicOps 特性,开发者可以在原生的Scala 类中使用BasicOps 中定义的类型、语句和结构来描述硬件设计.ScalaHDL 的设计应用了Lightweight Modular Staging[48]的思想,即ScalaHDL 的翻译通过Scala 的反射机制,依据运行时扫描类定义中实体得到的类型信息,来生成对应的Verilog 描述.在一个硬件定义类中,会包含两部分代码:硬件定义和原生Scala 语句.翻译时,包含ScalaHDL 特殊硬件类型的语句会被识别为硬件描述,而原生的Scala 代码则会被执行并发挥类似于编译指令的功能.这样的方式使得我们可以很方便地利用类型系统,将RTL抽象在Scala 中进行映射.具体来说,硬件定义实体的类型可以为模块、语句块、语句和值,其中语句块包括同步语句块和异步语句块.
SimulationSuite 与SimulationSchedule 相结合实现了软件模拟功能.由于我们在硬件类型实现中包含了不同类型在模拟时的行为,因此可以通过事件接口直接驱动硬件定义类实例的运行来实现功能模拟,这极大地简化了软件模拟器的实现.通过与实现好的基础通信驱动进行对接,测试平台也可以针对真实硬件进行测试.
要提供简便的软硬件集成支持,基本思路是通过抽象和包装将繁琐的底层数据传输细节隐藏起来.在Verilog 中我们通过用Scala 实现的基础通信驱动和根据用户模块定义自动生成的硬件数据接口层来完成软硬件的对接和传输细节的隐藏.图5 展示了VeriScala 框架中的运行时系统.在软件方面,Scala 实现的基础通信驱动包装内核中的PCIe DMA 驱动,将上层Scala 定义的数据结构序列化后传给内核模块,并通过相应的接口控制PCIe 通道的负载.应用可以在基础通信驱动的基础上开发特定应用的数据传输后端来实现类似函数调用语义的硬件调用,也可以直接使用基础驱动来进行数据传输.在硬件方面,我们设计了通用的接口层实现库来处理底层传输协议握手和数据缓存.通过用户顶层设计的接口定义,ScalaHDL 的翻译器可以在生成硬件设计时自动识别接口信号并进行相关的适配.同时在调试时,用户可以指定需要监视的信号,让翻译器额外生成调试服务硬件,通过与软件驱动配合来实现类似于GDB(GNU debugger)风格的硬件调试工具.
Fig.5 The runtime system图5 运行时系统
基于之前两节的阐述,VeriScala 框架中形成了如图6 所示的代码流程.图6 中以简单的加法器定义为例,在文件Adder.scala 中首先定义模块的容器类Adder 及其构造接口,然后在类中定义模块add,add 的逻辑由一个同步块来描述,由于加法器的功能简单,该同步块中仅包含一个条件分支语句.事实上,在Scala 中,我们可以将运算函数作为参数来传递,如文件Arithmetic.scala 中定义的Arithmetic 类可以在实例时接受一个函数f作为参数来生成不同的算术模块,这给硬件定义代码的复用提供了方便.对于定义好的硬件,开发者可以利用VeriScala 中的软件模拟器,通过编写测试平台和测试代码,进行软件模拟,也可以通过生成的一系列Verilog 代码,利用对应厂商的工具链和VeriScala 的运行时框架进行硬件测试并加速系统构建.
为了评估ScalaHDL 的生成代码质量,我们针对不同复杂度的常用电路分别使用ScalaHDL 和Verilog 进行定义,并根据Xilinx Vivado 软件编译自动生成代码和手动编写代码的资源消耗报告,得到的统计结果见表2.
Fig.6 Code overview图6 代码流程总览
Table 2 Resource consumptions of both variants (generated Verilog code and direct Verilog code)表2 生成的Verilog 代码与手写Verilog 代码的资源消耗对比(生成Verilog 代码/手写Verilog 代码)
我们发现,ScalaHDL 和Verilog 在定义表中电路时完全消耗相同的硬件资源.由于ScalaHDL 与Verilog 处于相同的抽象层次,这样的结果与预期相吻合.
为了评估VeriScala框架的实用性,我们基于VeriScala构建了典型的数据库过滤器加速系统,该应用使用Scala编写,将Scala 管理的主机数据库表项发送给FPGA 处理,再读回过滤后的数据.数据传输通过PCIE 3.0 接口完成.作为对比基准,我们同样基于Scala 实现了使用CPU 完成的过滤程序.该测试使用的硬件配置见表3.
Table 3 Experiment environment表3 测试环境
通过统计软件应用过滤512MB 随机数据的平均(1 000 次)任务运行时间,可以得到表4 展示的随单次传输块大小的增加而变化的加速比,受限于实验使用的FPGA 资源总量和我们采用的过滤器设计,单次传输32KB 是我们能在250MHz 下运行的最优值.从实验结果来看,VeriScala 框架能够有效地构建CPU+FPGA 的加速系统.
Table 4 Performance comparison between FPGA accelerated filter and CPU filter表4 FPGA 加速过滤器和CPU 过滤器的性能对比
2009 年,Martin 和Smith 在他们的综述文章中指出,当时不存在一种开发框架能够同时支持所有的领域:控制流和数据流、ASIC 和FPGA、随机逻辑和结构化模块,以及硬件、软件和软/硬协同各自的设计.他们指出,一个完整的能够探索整个设计空间的集成框架是设计师们的终极理想,同时也是未来的发展方向[18].经过10 年的发展,已经形成了如图1 所示的工具层次,高层次综合和高级硬件描述语言似乎都有潜力达到这一终极目标,而又有各自的不足.其中,高层次综合有难以处理复杂控制流和随机逻辑的缺点,使其需要高级语言框架外的经典硬件描述语言来辅助优化;而高级硬件描述语言则依然处于较低的抽象层次,其开发效率瓶颈和软硬件的语义鸿沟没有完全消除.那么一个直接的想法是,这两者能不能结合起来呢?
从图2 中我们可以发现,高级硬件描述语言和高层次综合并不完全独立.高级硬件描述语言将高级语言定义的寄存器传输级描述翻译成Verilog 或者VHDL,而高层次综合则是将高级语言定义的计算模型翻译成Verilog 或者VHDL.如果我们在高级硬件描述语言的基础上构建高层次综合工具,使得高层次综合首先生成高级硬件描述语言定义的硬件,然后再生成对应的经典硬件描述语言代码,或者我们将高层次综合功能实现为高级硬件描述语言的一个特殊库,是不是这样就能享受两者共同的优势呢?基于这个想法我们可以得到如图7 所示的架构.
Fig.7 The future architecture图7 未来架构
在该框架中,我们可以在同一种高级语言的上下文中进行硬件与软件设计,一方面,硬件设计可以兼顾控制流与数据流,另一方面,软硬件集成变成了简单的库调用.该架构的实现在很大程度上可以复用现有的研究成果,以ScalaHDL 为例,要想支持某种特定硬件的高层次综合,需要做的是,实现一个Scala Trait 来扩展已有的硬件类型和硬件定义语句,以及定义这些新的抽象在翻译时的机制.这一点对于许多基于Java、Scala 等可扩展性强的语言实现的框架来说都会有类似的解决方案.
硬件及硬件加速系统开发工具的选择取决于开发质量与开发效率的平衡,当硬件资源成为瓶颈时,硬件设计质量就会发挥更大的影响,而当硬件资源不是瓶颈时,开发效率则会发挥更大的影响.因此,在开发工具的发展历史中,一种抽象层次不会被轻易淘汰,人们往往希望使用提供更大设计空间的开发工具来面对复杂的需求与限制.在当前环境下,我们无法判断是高层次综合还是高级硬件描述语言会得到更广泛的应用,两者可能会长期共存.因此,可以推测,类似于图7 所示的综合开发框架是未来合理的发展方向.