Verilog/System Verilog 硬件设计语法说明
SV通常语法说明
声明相关语法
- `include
`include指令用于在代码行中包含任何其他文件的内容,被包含的文件即可以使用相对路径定义,也可以使用绝对路径定义。
`include "/path/file1"
`include "./path/file2"
要包含文件需要用双引号括起来,编译时会用包含文件的内容来替换该行。
包
- 包的定义
package definition;
parameter WIDTH = 32;
typedef struct{
logic clk;
logic reset;
} clk_rst;
enum {IDLE,WAIT,READ,WRITE} cstate,nstate;
endpackage
- 引用包的内容
import definition::WIDTH;
import definition::clk_rst;
import definition::cstate;
import definition::*;
如果仅仅导入cstate,那么里面的枚举元素IDLE,WAIT,READ,WRITE不能够被调用,所以需要用通配符导入。
文本值和数据类型
- 文本值赋值
SV相比Verilog增加赋值语法的功能
'1的功能是指将每一位全用1填充。 - 有无符号修饰
-
有符号数是以补码形式表示的(正数的补码 = 源码,负数的补码 = 反码+ 1)。
-
运算中如果有无符号数,那么都按照无符号数来进行运算。
-
无符号数加减法时,结果位宽加一,且结果也是无符号数;有符号数加减法时,结果位宽加一,结果是有符号数。
-
乘法中结果位数等于两数位数相加,有符号数向前补符号位
-
signed决定了操作数扩位的问题。
- 变量初始化确定性
SV中变量初始化将在仿真开始前进行,增加了确定性 - 强制类型转换
<type>‘(<expression>) 将一个值强制转换成任意数据类型
<size>’(<expression>) 将一个值强制转换成任意向量宽度
<sign>'(<expression>) 将一个值强制转换成有符号或无符号数
- SV的变量
- 四态变量
- logic
logic是一个四态变量(0、1、Z和X)
logic不能够被多驱动,所以inout要被定义为wire类型
- logic
logic [63:0] data;
- 两态变量
- bit
- byte
- shortint
- int
- longint
- const
一个const常数实质上是一个只能被初始化的变量。因为const形式的常数是在运行期间确定的数值,而不是在确立期,所以一个const常数可以在任意处声明。
const int N = 1024;
枚举数据类型
- 枚举数据类型
Verilog里面不存在枚举数据类型,只能通过用parameter来给标签赋一个常数值。这意味着工具无法限定信号的有效值只是这些常数。
当采用枚举类型的时候,所有的软件工具,包括仿真、综合和形势验证,都用相同的方式解释对于枚举类型变量合法值的约束。
enum {IDLE,WAIT,READ,WRITE} cstate,nstate;
- 枚举类型标签序列
格式 | 功能 |
---|---|
state | 创建单个标签 |
state[N] | 创建标签序列,state0,state1,…stateN |
state[N,M] | 创建标签序列,由stateN开始,stateM结束 |
- 枚举类型值
缺省情况下,枚举类型列表中的第一个标签表示数值0,第二个是1,以此类推。SV也可以显示地说明列表中标签的值。
enum { IDLE = 1,
WAIT = 5 } cstate,nstate;
此外,枚举类型的基类是int,因此赋值的时候需要满足基类设置。
- 枚举类型的专用方法
格式 | 功能 |
---|---|
.first | 第一个成员值 |
.last | 最后一个成员值 |
.next(N) | 下一个成员值 |
.prev(N) | 上一个成员值 |
.num | 枚举列表中元素的个数 |
.name | 返回枚举标签值 |
用户自定义类型
typedef int unsigned unit;
typedef struct{
logic clk;
logic reset;
} clk_rst_t;
命名习惯是在用户自定义类型后面加上_t作为后缀。
结构体
- 结构体的声明和初始化
typedef struct{
logic clk;
logic rst_n;
logic [31:0] rdata;
logic [15:0] rdata_h;
} block_a_t;
block_a_t A = '{0,0,32'b0,16'hFFFF};
- 结构体赋值
A = '{rst_n:0, clk:0, rdata:32'b0, rdata_h:16'hFFFF};
A = '{default:0, rdata_h:16'hFFFF};
- 压缩和非压缩结构体
packed可以声明一个压缩结构体
typedef struct packed{
logic clk;
logic rst_n;
logic [15:0] rdata_;
} block_a_t;
clk | rst_n | rdata |
---|---|---|
17 | 16 | [15:0] |
block_a_t[17]就是block_a_t.clk
但是压缩结构体只能包含整数值
- 通过端口传递结构体
结构体必须首先使用typedef定义,或者在$unit域中导入包
联合体
联合体只能存储一个值
常见应用是同一个数据值的不同形式表现
压缩联合体可以综合,每个联合体成员的位数是相同的
数组
- 非压缩数组
- 声明
logic [31:0] data [1024];
logic [31:0] data [0:1023];
- 初始化及赋值
logic data [0:1] [0:3] = '{'{0,1,2,3},'{4,5,6,7}};
- 压缩数组
- 声明
logic [3:0] [7:0] data; // 4个8位字
- 初始化及赋值
logic [3:0] [7:0] data = 32'h0;
- 数组构成的数组
logic [3:0] [7:0] mem [0:1023]; // 由32位元素组成的非压缩数组,每个元素都是由4个字节组成的压缩数组
索引规则:先引用的是非压缩数组的维度,从左到右;其次引用压缩数组的维度,从左到右。
mem[511][1][5] = 1'b1;
SV过程块
SV中有不同类型的块,如初始块(initial block)、始终块(always block)、顺序块(sequence block)、并行块(parallel block)等。这些块可以嵌套使用,并且可以有一个名称。
命名块的作用:命名块可以使得代码更加清晰,便于阅读和理解。在仿真和调试过程中,命名块可以帮助识别特定的代码区域,便于定位问题。
块名的规则:块名必须遵循SV的标识符命名规则,即以字母或下划线开头,后面可以跟字母、数字或下划线。
块名的唯一性:在同一作用域内,块名必须是唯一的,不能有重复。
- always_comb
在所有initial和always过程快启动后,always_comb块会在仿真的零时刻自动触发。
所有分支都有执行,否则会造成锁存,可以通过always_comb在开始时为所有变量写入默认值
always_comb begin:demo
a = 1'b0;
if(en == 1'b1) begin
a = 1'b1;
end
end:demo
- always_latch
在所有initial和always过程快启动后,always_latch块会在仿真的零时刻自动触发。 - always_ff
改进的case语句
- unique case
只有一个条件选项与条件表达式匹配
必须有一个条件选项与条件表达式匹配 - priority case
至少有一个条件选项的值与条件表达式匹配
如果有多个条件选项的值与条件表达式匹配,必须执行第一个匹配分支
改进的if…else判断语句
- unique if
- priority if
SV状态机模型
- 使用枚举类型表示状态编码
enum bit[2:0] { IDLE = 3'b001,
WAIT = 3'b010,
WORK = 3'b100 } cstate,nstate;
尽量使用枚举列表中的标签对枚举变量赋值
- 采用反向case语句进行one-hot状态机建模
- 采用枚举类型+unique case
module three_stage_fsm(
input clk,
input rst_n,
input in,
output logic out
);
enum logic[2:0] { IDLE = 3'b001,
WAIT = 3'b010,
WORK = 3'b100 } cstate,nstate;
always_ff @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
cstate <= IDLE;
end
else begin
cstate <= nstate;
end
end
always_comb begin: three_stage_fsm
unique case (cstate)
IDLE: begin
if (in == 1'b1) begin
nstate = WAIT;
end
else begin
nstate = IDLE;
end
end
WAIT: begin
if (in == 1'b1) begin
nstate = WORK;
end
else begin
nstate = WAIT;
end
end
WORK: begin
if (in == 1'b1) begin
nstate = IDLE;
end
else begin
nstate = WORK;
end
end
default: nstate = IDLE;
endcase
end: three_stage_fsm
always_ff @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
out <= 1'b0;
end
else if(nstate == WORK) begin
out <= 1'b1;
end
else begin
out <= 1'b0;
end
end
endmodule
特殊语法说明
本节持续更新一些遇到的不理解的verilog/SV语法,并加以说明
$clog2函数可以直接在Verilog代码中使用,以确定表示给定数值范围所需的最小位宽
function的特性:
缺点:内部难以debug
优点:看起来简洁
always @ (*):
可用于反复赋值,长逻辑赋值