小谈SystemVerilog可综合语法在FPGA逻辑开发中的实践
1. SystemVerilog 可综合语法概述
SystemVerilog是Verilog语言的拓展和延伸,结合了来自VHDL、C++的概念,还有验证平台语言和断言语言,构成了用于设计和验证的完整语言。本文探讨SystemVerilog 在硬件设计中的可综合特性。
SystemVerilog标准拓展了Verilog验证和硬件建模能力。Verilog语言支持不同级别的硬件设计建模,包括系统级,算法级,寄存器级,逻辑级,门级,电路开关级设计,并且经过设计实践迭代了三个verilog标准,即Verilog-1995, Verilog-2001和Verilog-2005。 SystemVerilog 语言针对硬件设计描述和验证特性不断更新丰富,特别是最新的验证特性和方法,不断被更新进SystemVerilog语言版本中。SystemVerilog语言目前已经发布过IEEE 1800-2005 SystemVerilog、IEEE 1800-2009 SystemVerilog、IEEE 1800-2012 SystemVerilog和IEEE 1800-2017 SystemVerilog四个版本。
面向可综合设计的主要优化有如下几点:
1)添加接口(interface)从而将通信和协议检查进一步封装。接口在大型设计互联上具有简化连接,提高效率的作用。
2)添加类似C语言的数据类型,例如int、byte。在硬件设计中int 和byte比较少用,对于数据类型使用自定义类型更适用于硬件电路的使用场景。
3)添加用户自定义类型,枚举类型,结构体类型。自定义类型,提供了更多灵活性,提供了场景类型扩展能力。结构体类型,是多个信号内聚耦合的表达,提供了收缩性和灵活性。枚举类型映射了具体的常量,常用于状态机中。
4)添加类型转换($case(T, S)或者’())。这个在硬件设计中基本很少使用。
5)添加包(package)从而使得多个设计之间可以共享公共类型和方法。包进一步提供了更上一层共享使用的各种类型(枚举、结构体等),函数,宏和参数等。使用上需要注意包的作用域和调用方法。简化使用,可以将包中的内容放入头文件中,代替使用包(package)。硬件逻辑模块中调用头文件,实现对共享内容的使用。
6)添加方便的赋值操作符和运算操作符,例如:++、+=、===。 这些操作符虽然提供了便利性,但是最好还是按照更明确的传统写法,减少后续的维护负担。
7) 添加priority case和unique case语句。unique case相当于同时使用full_case和parallel_case ,而priority case等效使用full_case 。 这两种指定 case类型的使用,除非对结果非常明确和确定,否则可能带来意想不到的结果。通常情况下,还是建议使用传统的带default的case语句。
8)添加always_comb、always_latch和always_ff等过程语句块。细化always语句块,明确区分了组合逻辑和时序逻辑,方面了理解和维护逻辑代码。
2. Fpga开发中常用的SystemVerilog可综合语法特性
2.1 枚举类型
自定义枚举类型如图1所示,显式指定枚举的成员比特位宽和具体数值,而不是依靠默认值,方便代码维护。在硬件设计中,建议不要使用枚举的默认方式。
图1.自定义枚举类型
2.2 结构体类型
自定义结构体类型如图2所示,将ipv4头定义成结构体,方便使用。使用verilog语言实现相同功能,需要一个多比特的一维数组,再通过一系列的参数定义每个字段的起始位置和长度。Verilog语言数组实现方式,若是某一字段调整了比特位宽,则基本每一项都要调整比特位置,维护复杂繁琐。SystemVerilog的结构体方式,将若干独立的成员结合在一起,不用代码层面去维护每个成员的存储比特位置,可以方便扩展和调整,仿真时还能方便查看到每一个成员的数值。将一组信号结合到一起,使用到模块的输入和输出上就是接口。所以,结构体可以放到模块接口上,代替interface使用。
图2.自定义结构体
2.3 Logic 类型
Verilog描述硬件电路经常使用wire和reg 。wire类型通常用于assign 语句和模块间线网连接。Reg类型可以综合成信号线,还可以是触发器。这在设计实现时又和阻塞赋值和非阻塞赋值结合,容易混淆,引入硬件设计描述的不规范。
在System Verilog中我们可以把wire和reg替换成logic。至于综合成什么,交给综合工具。但是硬件设计描述的目标应该是显式明确的,规范明显的,此时最好结合使用system verilog 中的 always_ff 和always_comb,以明确表达设计目标是组合逻辑还是时序逻辑。
2.4 Always过程语句块细分
Always过程语句块,进行细分成always_ff, always_latch和always_comb,显式表达设计目标电路。这样,就明确了时序逻辑,组合逻辑和锁存器电路。明确描述硬件设计意图,给硬件电路的仿真验证和综合实现带来了便捷和准确。
2.5 Function 函数
Function 函数也是可综合的,通常用于构建标准化的函数,比如求取crc校验值,数值计算等。SystemVerilog 中的function 可以有input , output , 返回类型也可以是void类型,可以方便的形成多个返回结果。图3的function函数返回了角度和幅度两个数值。
图3. SystemVerilog 中的function函数示例
2.6 For 循环
For循环在verilog语言中已经支持。在SystemVerilog中更是广泛使用,以方便逻辑编码,快速实现逻辑块代码的复制并发处理。图4所示代码通过for循环方式创建了3个地址寄存器的赋值过程。
图4. SystemVerilog中的for循环示例
2.7 Interface 接口
接口(interface)是SystemVerilog引入的很重要的特性,目前在绝大多数验证环境或者设计中都会出现。接口最直接的作用就是将一组相关的信号封装到一起,特别是一些标准协议的接口信号,比如常见的AMBA AXI/AHB/APB等等。接口价值在于方便验证环境组件的连接,或者设计中逻辑模块的连接,降低连接出错的概率。
接口并不只是提供一组信号这么简单,它还可以包含modport、clocking block、parameter、过程语句块、断言、覆盖组(covergroup)、函数和任务等。接口用于可综合的设计中通常使用参数化接口,并且包含modport,可选择包含时钟和复位信号。
以FPGA设计中经常使用的avalon stream 包接口为例,如图5所示包接口的定义。包接口中包括基本的sop(start of packet), eop(end of packet),dat(data of packet), mty(empty of data), val(valid of packet), rdy(ready of interface),还可以增加自己设计定义的chn(channel),err(error)和sbd(sideband)等信号,增加接口的灵活性和丰富性。另外error 和sbd 信号也可以定义成特定数据类型,精确到每个具体信号,而不是多比特数据的聚合。图5的示例是一个参数化的接口。
图5 avalon stream packet 接口示例
在FPGA逻辑开发中,也可以使用结构体代替接口定义一组信号集合,只是需要区分定义输入和输出两个结构体。