第4章 用户自定义和枚举数据类型
Systemverilog允许用户自定义新的线网和变量类型,从而对Verilog语言作了重要的扩展。用户自定义类型在保证正确性和可综合性的前提下,允许在更高抽象级上进行复杂的建模设计。使用SystemVerilog的用户自定义类型,可以在较少的代码行中建模更多的设计功能,这增加了代码的自文档性(self-documenting ),更容易阅读。
本章介绍的增强功能包括:
•使用typedef创建用户自定义类型
•使用枚举创建枚举类型
•使用枚举值
4.1用户自定义类型
Verilog语言不为用户提供扩展线网和变量类型的机制。虽然现有的Verilog类型对RTL和门级别建模非常有用,但它们不提供可用于更高层抽象的类似C语言的变量类型。Systemverilog为系统级和架构(architectural)级上的建模添加了许多新类型。此外,SystemVerilog增加了用户自定义新的线网和变量类型的能力。
用户自定义类型使用typedef关键字创建,如同C语言。用户自定义类型允许从现有类型创建新的类型定义。一旦定义了新类型,就可以声明新类型的变量。例如:
4.1.1 局部typedef类型
用户定义的类型可以在局部、程序包中或编译单元域内的外部定义。当用户定义类型仅在设计的特定部分内使用时,可以在表示设计的该部分的模块或接口内进行类型定义。第10章中介绍了接口。在下面的代码段中,声明了名为nibble的用户定义类型,该类型用于称为alu的模块中的变量声明。由于在局部定义了nibble类型,因此只有alu模块可以查看该定义。构成总体设计的其他模块或接口不受局部定义的影响,并且可以出于其他目的使用相同的nibble标识符,而不会受到模块Alu中局部typedef定义的影响。
4.1.2 共享typedef类型
当用户自定义的类型要在许多不同的模型中使用时,类型定义可以在包中声明。然后可以直接引用这些定义,或者将它们导入使用用户自定义的类型的每个模块、接口或程序块。
示例4-1:直接引用包中的typedef定义
typedef 定义也可以在外部声明,即在编译单元域中声明。外部声明就将typedef语句放在任何模块/接口或程序块的外面。
如果一个用户自定义类型会在很多不同的模块或接口是使用,可以把typedef声明放在
u
n
i
t
编译单元域中声明。如果程序包包含许多
t
y
p
e
d
e
f
,而不是将特定程序包项导入
unit编译单元域中声明。 如果程序包包含许多typedef,而不是将特定程序包项导入
unit编译单元域中声明。如果程序包包含许多typedef,而不是将特定程序包项导入unit编译单元空间,则可以将程序包通配符导入到$unit中。
4.1.3用户自定义类型的命名习惯
为了使源代码更易于阅读和维护,通常的命名惯例是用户自定义类型命名用字符“_t”结尾。
4.2枚举数据类型
枚举数据类型提供了一种方式来声明一个具有特定允许值列表的抽象变量,每一个值都有一个确定的用户自定义名字,即标签(label)。
enum {red,green,blue} RGB;
标签值的Verilog风格
Verilog语言没有枚举类型,为了给数值创建一个标签,必须定义parameter常数来表示每个值,并且将值赋给常数(标识符)。`define文本替换是另一种把标识符与值对应的方法。
使用常量值的变量(前一个示例中的State和NextState)必须声明为标准的Verilog变量类型。这意味着软件工具不能将这些信号的有效值限制为仅是这些常量的值。以上示例中的State和NextState不限制值为3或值(一个或多个位设置为X或Z)。因此,模型本身必须对这些值增加一些限制检查。至少,需要一个合成“fall case”编译指令来通知编译工具,状态变量只能使用case列表的常量值。但是,综合编译指令的使用不会影响仿真,这可能导致仿真行为和由综合产生的结构级设计并不相同。
标签值的SystemVerilog风格
Systemverilog增加了枚举类型,使用enum关键字,声明形式如下:
enum {WAITE, LOAD, STORE} State, NextState;
在本示例中,变量State和NextState只能是WAITE、LOAD和STORE的三种有效值。所有软件工具,包括仿真、综合和形式验证,将以相同的方式解释对于枚举类型变量合法值的约束。
从包中导入枚举类型
注意:导入枚举类型定义名时不会自动导入枚举值标签。
为了使枚举类型标签可见,可以显式导入每个标签,或用通配符导入整个包。
4.2.1 枚举类型标签序列
除了指定一组唯一的标签外,SystemVerilog还提供了两种便捷方法,可以在一个枚举类型列表中指定标签范围。
表4-1:指定一个枚举列表标签序列
以下示例使用标签创建枚举列表:RESET、S0S4、W6W9.
enum {RESET,S[5],W[6:9]} state
4.2.2 枚举类型标签的作用域
枚举类型列表中的标签在其作用域内必须是唯一的,可以包含枚举类型声明的作用域包括编译单元、模块、接口,任务、函数、begin…end块和fork…join块。
下面的例子,枚举标签GO在同一作用域里使用了两次,导致错误。
module FSM(…);
enum {GO, STOP} fsm1_state;
…
enum {WAIT, GO, DONE} fsm2_state;
…
endmodule
修改办法之一,至少将一个枚举类型声明放到一个begin…end块中。
module FSM (...);
...
always @(posedge clock)
begin: fsm1
enum {STOP, GO} fsm1_state;
...
end
always @(posedge clock)
begin: fsm2
enum {WAITE, GO, DONE} fsm2_state;
...
end
...
4.2.3 枚举类型值
默认情况下,枚举列表中标签表示的实际数值是int类型的整数。枚举列表中的第一标签以值0表示,值为1的第二标签,值为2的第三标签,以此类推。
SystemVerilog允许显式声明枚举列表中每个标签的值。如果需要,可以对抽象枚举类型进行修改,以描述更详细的硬件特性。例如,状态机序列可以显式建模为one-hot值、one-cold值、Johnson-count、gray-code或其他类型的值。
enum {ONE = 1,FIVE = 5,TEN = 10 } state;
无需在枚举列表中指定每个标签的值。如果未指定,则表示每个标签的值将比之前的标签增加1。在下例中,显式地给予标签A值1,B自动给予递增值2,C递增值3。x是显式定义为具有值24,并且和z被分别给出递增值25和26。
enum {A=1, B, C, X=24, Y, Z} list1;
枚举列表中的每个标签必须具有唯一值。
enum {A=1, B, C, D=3} list2; // ERROR
4.2.4 枚举类型的基类
枚举类型是具有一系列标签值的变量或线网,因此,枚举类型具有一个Verilog或SystemVerilog的基类,其默认的基类是int类型,32位两态类型。
SystemVerilog允许对枚举类型的基类进行显式的声明。
enum bit {TRUE, FALSE} Boolean; // enumerated type with a 1-bit wide,2-state base type
enum logic [1:0] {WAITE, LOAD, READY} state; // enumerated type with a 2-bit wide, 4-state base type
如果对显式定义枚举类型的枚举标签赋值,那么这个值的宽度必须和基类的宽度相同。
将一个与枚举类型声明的基类宽度不同的值赋给一个枚举标签是错误的。
enum {WAITE = 3’b001, // ERROR!
LOAD = 3’b010,
READY = 3’b100} state;
如果枚举列表中标签数超过了基类所能代表的宽度,也是错误的。
enum logic {A=1’b0, B, C} list5;// ERROR: too many labels for 1-bit size
如果枚举值是4态数据类型,将枚举值赋为X或Z是合法的。如果一个枚举标签被赋为X或Z,下一个标签必须被显示的赋值。试图由赋值为X或Z的标签的值加1来自动赋值是错误的。
enum logic {ON=1’b1, OFF=1’bz} out;
enum logic [1:0] {WAITE, ERR=2’bxx, LOAD, READY} state; // ERROR: cannot determine a value for LOAD
4.2.5 自定义和匿名枚举
枚举类型可以声明为用户自定义类型。这提供了一种便捷的方法来声明几个具有相同枚举值集的多个变量或线网。
使用typedef声明的枚举类型通常称为自定义类型枚举类型。如果不使用typedef,则枚举类型通常称为匿名枚举类型。
typedef enum {WAITE, LOAD, READY} states_t;
states_t state, next_state;
4.2.6 枚举类型操作的强类型检验
大多数Verilog和SystemVerilog变量类型是弱类型,任何类型的值都可以赋给一个变量。该数值将按照Verilog或SystemVerilog标准中规定的转换规则自动转换为变量类型。
枚举类型是半强类型。一个枚举类型只可以进行下列赋值。
•枚举类型列表中的一个标签
•同类枚举类型的其它变量
•通过$cast转换成枚举类型变量的数值
对枚举类型值执行操作时,枚举值自动转换为表示枚举类型列表中标签的基类型和内部值。如果枚举类型的基类未显式声明,则基类和标签将默认为int类型。
typedef enum {WAITE, LOAD, READY} states_t;
states_t state, next_state;
int foo;
WAITE是值为0的int类型,LOAD是值为1的int类型,READY是值为2的int类型。
state = next_state; // legal operation
foo = state + 1; // legal operation
state = foo + 1; // ERROR: illegal assignment
state= state +1; // ERROR: illegal assignment
state++; // ERROR: illegal assignment
next_state + = state;// ERROR: illegal assignment
4.2.7 将表达式强制转换为枚举类型
一个操作的结果可以转换为一个枚举类型,然后赋值给同一类型的枚举类型变量。可以使用SystemVerilog的强制转换操作符或动态#cast系统函数。
typedef enum {WAITE, LOAD, READY} states_t;
states_t state, next_state;
next_state = states_t’(state++); // legal
$cast(next_state, state + 1); // legal
使用转换运算符和动态$cast系统函数之间存在重要区别。强制转换运算符总是进行强制转换操作和赋值,而不检查被赋的值是否在枚举类型设置的合法范围内。在例4.3中,state的标签READY,其值表示为2,将其增大1会导致整数值3。将此值赋值next_state就会超出next_state举类型列表的数值范围。
此超出范围值可能导致不确定行为。不同软件工具对超出范围值的处理方式不同。如果赋值超出范围值,则枚举变量在RTL模型综合前仿真时最终存储的实际值,可能与综合后所产生的门级网表的功能不同。
为了避免不明确的行为,编码时防止超出范围的枚举类型赋值非常重要。静态转换操作符无法始终检测何时分配超出范围值,因为转换操作符不会执行运行时错误检查。
动态 c a s t 系统函数在修改目标变量之前检查表达式结果是否是一个合法值。在上一个示例中,如果 s t a t e + 1 对 n e x t s t a t e 是一个范围之外的值,那么 cast系统函数在修改目标变量之前检查表达式结果是否是一个合法值。在上一个示例中,如果state+1对next_state是一个范围之外的值,那么 cast系统函数在修改目标变量之前检查表达式结果是否是一个合法值。在上一个示例中,如果state+1对nextstate是一个范围之外的值,那么cast(next_state, state + 1); 不会修改next_state的值,而且会报告一个运行错误。 这两种转换方式需要工程师在建模时进行有效的折中。动态转换由于其运行时的误差检查而安全。但是,这种运行时检查会给操作增加一些处理开销,这可能会影响软件工具性能。还有$cast系统函数可能不可综合。编译时静态转换操作符不执行运行时检查,因此静态转换操作可以优化以获得更好的运行时性能。
用户可以根据模型的性质选择使用哪种转换方法。如果已知不会发生超出范围值,则可以使用更快的静态转换操作符。如果存在超出范围的可能性,则可以使用更安全的$cast系统函数。注意,也可以使用SystemVerilog assert断言捕获超出范围值,但断言不会阻止超范围外赋值的发生。在配套的书《SystemVerilog for Verification》中介绍了各种断言。
4.2.8 枚举类型的专业系统任务和方法
Systemverilog提供了多个内置函数(称为方法)来循环访问枚举类型列表中的值。这些方法会自动处理枚举类型的半强类型性质。使用这些方法,不必知道列表中的名词和值。
这些处理枚举列表的专用方法以类似于C++类方法的方式调用。即方法的名称附在枚举变量名字后面用一个“.”分开。
<枚举变量名>.first – 返回枚举列表第一个成员的值
<枚举变量名>.last – 返回枚举列表最后一个成员的值
<枚举变量名>.next() – 返回枚举列表中下一个成员的值,可以用一个整数作为next的参数,这种情况返回后面第N个成员的值
<枚举变量名>.prev() – 返回枚举列表中前一个成员的值
<枚举变量名>.num – 返回枚举列表中元素个数
<枚举变量名>.name – 返回枚举变量值对应的字符串,如果这个值不在枚举变量列表中,则返回一个空字符串
例4.5说明了使用上面列出的一些枚举方法来建立一个状态机模型。示例是一个简单的0-15置信度计数器,其中: in_sync输出初始为0;当计数器达到8时置位,计数器到0清零;如果输入compare和synced都为假,则计数器保持当前值;如果输入compare和synced都为真,则计数器+1(但不能超过15);如果输入compare为真而synced为假,则计数器减2(但不能低于0)。
4.2.9 打印枚举类型
打印枚举类型值可以打印为标签的枚举内部值,也可以打印为标签的名称。直接打印枚举变量会打印出变量的内部值。使用枚举类型的方法name可以读取代表当前值的标签名称,并返回包含名称的字符串,这个字符串可以用$display打印出来。
4.3 小结
C语言风格的typedef定义允许在Verilog和SystemVerilog中预先定义的数据类型或其他用户自定义类型的基础上定义新的数据类型。用户自定义的类型可用作模块端口,也可以用于任务和函数参数传递。
枚举类型允许使用一组满足限制的有效值声明变量,并且使用抽象名称而不是面向硬件的逻辑值来表示这些值。与Verilog比,枚举类型的抽象能力更高,可以用更少的代码建模更大的设计。如果需要,可以在枚举类型声明添加硬件实现细节,例如将表示状态机状态的枚举类型列表赋值为one-hot编码值。
SystemVerilog还添加了class数据类型,支持面向对象风格的建模。类对象和面向对象的编程主要是用于验证的,并且当前不可综合。有关SystemVerilog类的细节和示例可在配套的图书《SystemVerilog for Ker cafioo l》中找到。
欢迎使用Markdown编辑器
你好! 这是你第一次使用 Markdown编辑器 所展示的欢迎页。如果你想学习如何使用Markdown编辑器, 可以仔细阅读这篇文章,了解一下Markdown的基本语法知识。
新的改变
我们对Markdown编辑器进行了一些功能拓展与语法支持,除了标准的Markdown编辑器功能,我们增加了如下几点新功能,帮助你用它写博客:
- 全新的界面设计 ,将会带来全新的写作体验;
- 在创作中心设置你喜爱的代码高亮样式,Markdown 将代码片显示选择的高亮样式 进行展示;
- 增加了 图片拖拽 功能,你可以将本地的图片直接拖拽到编辑区域直接展示;
- 全新的 KaTeX数学公式 语法;
- 增加了支持甘特图的mermaid语法1 功能;
- 增加了 多屏幕编辑 Markdown文章功能;
- 增加了 焦点写作模式、预览模式、简洁写作模式、左右区域同步滚轮设置 等功能,功能按钮位于编辑区域与预览区域中间;
- 增加了 检查列表 功能。
功能快捷键
撤销:Ctrl/Command + Z
重做:Ctrl/Command + Y
加粗:Ctrl/Command + B
斜体:Ctrl/Command + I
标题:Ctrl/Command + Shift + H
无序列表:Ctrl/Command + Shift + U
有序列表:Ctrl/Command + Shift + O
检查列表:Ctrl/Command + Shift + C
插入代码:Ctrl/Command + Shift + K
插入链接:Ctrl/Command + Shift + L
插入图片:Ctrl/Command + Shift + G
查找:Ctrl/Command + F
替换:Ctrl/Command + G
合理的创建标题,有助于目录的生成
直接输入1次#,并按下space后,将生成1级标题。
输入2次#,并按下space后,将生成2级标题。
以此类推,我们支持6级标题。有助于使用TOC
语法后生成一个完美的目录。
如何改变文本的样式
强调文本 强调文本
加粗文本 加粗文本
标记文本
删除文本
引用文本
H2O is是液体。
210 运算结果是 1024.
插入链接与图片
链接: link.
图片:
带尺寸的图片:
居中的图片:
居中并且带尺寸的图片:
当然,我们为了让用户更加便捷,我们增加了图片拖拽功能。
如何插入一段漂亮的代码片
去博客设置页面,选择一款你喜欢的代码片高亮样式,下面展示同样高亮的 代码片
.
// An highlighted block
var foo = 'bar';
生成一个适合你的列表
- 项目
- 项目
- 项目
- 项目
- 项目1
- 项目2
- 项目3
- 计划任务
- 完成任务
创建一个表格
一个简单的表格是这么创建的:
项目 | Value |
---|---|
电脑 | $1600 |
手机 | $12 |
导管 | $1 |
设定内容居中、居左、居右
使用:---------:
居中
使用:----------
居左
使用----------:
居右
第一列 | 第二列 | 第三列 |
---|---|---|
第一列文本居中 | 第二列文本居右 | 第三列文本居左 |
SmartyPants
SmartyPants将ASCII标点字符转换为“智能”印刷标点HTML实体。例如:
TYPE | ASCII | HTML |
---|---|---|
Single backticks | 'Isn't this fun?' | ‘Isn’t this fun?’ |
Quotes | "Isn't this fun?" | “Isn’t this fun?” |
Dashes | -- is en-dash, --- is em-dash | – is en-dash, — is em-dash |
创建一个自定义列表
-
Markdown
- Text-to- HTML conversion tool Authors
- John
- Luke
如何创建一个注脚
一个具有注脚的文本。2
注释也是必不可少的
Markdown将文本转换为 HTML。
KaTeX数学公式
您可以使用渲染LaTeX数学表达式 KaTeX:
Gamma公式展示 Γ ( n ) = ( n − 1 ) ! ∀ n ∈ N \Gamma(n) = (n-1)!\quad\forall n\in\mathbb N Γ(n)=(n−1)!∀n∈N 是通过欧拉积分
Γ ( z ) = ∫ 0 ∞ t z − 1 e − t d t . \Gamma(z) = \int_0^\infty t^{z-1}e^{-t}dt\,. Γ(z)=∫0∞tz−1e−tdt.
你可以找到更多关于的信息 LaTeX 数学表达式here.
新的甘特图功能,丰富你的文章
- 关于 甘特图 语法,参考 这儿,
UML 图表
可以使用UML图表进行渲染。 Mermaid. 例如下面产生的一个序列图:
这将产生一个流程图。:
- 关于 Mermaid 语法,参考 这儿,
FLowchart流程图
我们依旧会支持flowchart的流程图:
- 关于 Flowchart流程图 语法,参考 这儿.
导出与导入
导出
如果你想尝试使用此编辑器, 你可以在此篇文章任意编辑。当你完成了一篇文章的写作, 在上方工具栏找到 文章导出 ,生成一个.md文件或者.html文件进行本地保存。
导入
如果你想加载一篇你写过的.md文件,在上方工具栏可以选择导入功能进行对应扩展名的文件导入,
继续你的创作。
注脚的解释 ↩︎