Verilog HDL 语言
Verilog HDL 简介
硬件描述语言Hardware Description Language
是一种用形式化方法即文本形式 来描述和设计数字电路和数字系统的高级模块化语言
- Verilog HDL(Hardware Description Language)是一种硬件描述语言,用于建模、设计和验证数字系统,比如芯片(ASIC)、FPGA等。
- 它既可以用于描述电路结构(结构级建模),也可以描述电路行为(行为级建模)。
基本组成
- 模块(module):Verilog的基本单元,定义了输入输出端口和内部逻辑。
- 端口(ports):模块的输入(input)、输出(output)、双向(inout)信号。
- 信号类型(nets和variables):例如
wire
(连线类型),reg
(寄存器类型)。
数据类型
变量值
值 | 含义 |
---|---|
0 | 代表逻辑0 或 否条件 |
1 | 代表逻辑1 或 真条件 |
X | 代表未知的逻辑值(可能是0也可能是1) |
Z | 代表一个高阻态 |
0 在电路中一般是低电平
1 在电路中是高电平
X 一般是寄存器类型( reg )未初始化
Z 是线型( wire )变量未接驱动
(实际写代码时要避免出现X和Z)
wire(线网)
wire
表示一根连接线(物理连线)。- 它不能自己存值,通常用于连续赋值,只能连接别的东西,比如assign语句、模块的输出。
- 一般用于组合逻辑(纯逻辑运算)。
例子:
wire a; // 声明一根线,1-bit位宽
assign a = b & c; // a 由 b 和 c 按位与得到
wire d = 1'b0; //1代表1-bit位宽,'b'代表二进制,0是初值
reg(寄存器)
reg
是一个存储单元。- 在
always
块或initial
块里赋值。 - 不一定是真正硬件的"寄存器",但可以存住一个值直到下次变化。
例子:
reg rstn;
initial begin
rstn = 1'b0;
#100; // 等待100单位时间(单位根据仿真设置,比如ns或ps)
// # 是延时操作
rstn = 1'b1;
end
reg d; // 声明一个寄存单元
always @(posedge clk) begin
d <= a; // 非阻塞赋值,时序逻辑 每次时钟上升沿,d取a的值
end
🔔 注意:
wire
用在assign
、模块之间连线reg
用在always
块里
向量vector
当你声明多位的 wire
或 reg
时,就叫向量(Vector)。
向量本质上就是:一组用一根名字表示的多位信号。
你可以整体操作,也可以单独取其中一位或几位。
wire [7:0] data; // 8-bit位宽线
reg [15:0] addr; // 16-bit位宽寄存器
多位信号可以通过下标访问:
a = data[0]; // 取最低位
b = data[7:4]; // 取高4位
用途 | 说明 |
---|---|
表示多位数据 | 比如8位数据总线、16位寄存器、32位地址 |
方便整体操作 | 可以一次赋值、比较、传递 |
位选取灵活 | 可以随时取某几位,比如取高字节、低字节 |
节省命名空间 | 不用每一位单独声明变量 |
[高位:低位]
是强制要求高位写左边。
数组 Array
在 Verilog 中允许声明 reg, wire, integer, time, real 及其向量类型的数组,数组的大小代表深度
reg y1 [11:0]; //是一个1-bit位宽,深度为12的寄存器
wire [7:0] y2 [11:0]; //是一个8-bit位宽,深度为12的线
reg [15:0] y3[0:1][0:3]; //二维数组
数组定义时,索引的定义可以自由选择,但通常使用 [高:低]
的写法。
写法 | 含义 | 应用习惯 |
---|---|---|
[高:低] (如 [11:0] ) |
数字从高到低排列 | 通常用于总线、数据字节流,符合大部分硬件设计习惯 |
[低:高] (如 [0:11] ) |
数字从低到高排列 | 有些数组或存储器设计喜欢用递增顺序 |
其他类型
类型 | 说明 | 是否可综合 | 特别说明 |
---|---|---|---|
integer | 32位有符号整数变量 | ✅ 可综合(但慎用) | 适合建模简单计数器,但综合成硬件时表现不够精准 |
real | 浮点实数变量 | ❌ 不可综合 | 只能用于仿真,比如仿真计算,不能转成硬件 |
time | 64位无符号整数,存仿真时间 | ❌ 不可综合 | 只用于仿真(比如记录时间点) |
realtime | 浮点数形式存时间 | ❌ 不可综合 | 和 time 类似,但有小数精度 |
string | 字符串,存储在 reg 中 | ❌ 理论可综合,但实际几乎只用于仿真 | 硬件几乎不会真的合成字符串 |
logic [n:0] | SystemVerilog扩展型变量,结合了wire+reg特性 | ✅ 可综合 | 现在新项目基本用 logic 代替 reg |
概念 | 解释 | 举例 |
---|---|---|
可综合(Synthesisable) | 能够被综合工具(如 Vivado、Quartus)转成真正的硬件电路(逻辑门、触发器、存储单元等) | wire, reg, logic |
不可综合(Non-Synthesisable) | 只在仿真阶段起作用,不能转成电路,硬件里不会存在对应结构 | real, time, realtime, string |
模块 module
- 模块(module)是 Verilog 的基本单位,相当于C语言的函数、Java的类,代表一个可以综合为实际硬件的功能单元。
- 每一个
module
可以是一个门电路、触发器、加法器、CPU子模块,甚至整个SoC。
基本写法
Verilog模块的结构由在 module 和 endmodule 关键词之间的 4 个主要部分组成:
- 端口定义 module 模块名 端口 1 端口 2
- I/O 说明 包括输入 (input) 、 输出 (output) 和双向(inout)
- 信号类型声明 声明信号的数据类型和函数声明 wire,reg, integer, real, time
- 逻辑描述 用来描述设计模块的内部结构和模块端口间的逻辑关系 。 常用
assign
语句 、always
块语句等方法实现
module 模块名(端口列表);
// 端口定义 (input output)
// 内部信号定义(wire, reg等)
// 电路描述(assign语句、always块等)
endmodule
例子:
module block1(a,b,c,d);
input a,b,c;
output d;
wire x;
assign d = a|x; //a或x 按位“或”运算
assign x = (b & ~c);
endmodule
module and_gate (input a, input b, output y);
assign y = a & b;
endmodule
意思:建了一个名字叫 and_gate
的模块,它有两个输入端口 a
、b
,一个输出端口 y
,输出是 a AND b
。
模块实例化
模块是具一个有特定功能的设计单元, 在电路综合时模块会被 转 换为相应的数字电路
给定模块一组输入, 模块会返回一组输出, 这意味着模块可以被重复使用, 由此来实现更加复杂的电路(相当于函数)
module mod1(input d, …);
// 模块内容
endmodule
module mod2;
wire data;
mod1 u0 (.d(data),…);
// 模块内容
endmodule
mod1 u0 (.d(data),…);
这句在实例化模型时,需要把本模块内的data
信号连接到mod1
模块的端口d
上。
位置关联(不推荐)
module my_module;
wire a, b, c;
and_gate u1 (a, b, c); // a 连接 .a, b 连接 .b, c 连接 .y
endmodule
问题:你必须知道子模块端口的顺序,一旦顺序改了容易出错。
命名关联(推荐)
and_gate u1 (
.a(a), // 上层的 a 连接到模块的 a
.b(b), // 上层的 b 连接到模块的 b
.y(c) // 上层的 c 连接到模块的 y
);
优点:
- 与顺序无关
- 更直观、可读性强
- 推荐用于所有复杂模块
带参数模块的实例化(parameter)
当模块带有 parameter
参数时,可以通过 #()
语法传值:
模块定义
module counter #(
parameter WIDTH = 8
)(
input clk,
output [WIDTH-1:0] out
);
...
endmodule
实例化方式
counter #(.WIDTH(4)) my_cnt (
.clk(clk),
.out(cnt_out)
);
多个实例的情况
可以实例化多个同一个模块:
and_gate U1 (.a(a1), .b(b1), .y(y1));
and_gate U2 (.a(a2), .b(b2), .y(y2));
每次实例代表“一个新的电路副本”。
实践建议 | 说明 |
---|---|
命名关联优于位置关联 | 尤其是模块接口复杂时 |
实例名推荐加前缀 | 如 u_and1 , u_mux4 |
参数化模块要注意 default | 给定默认值可提升复用性 |
用注释标出每个模块 | 增加结构清晰度 |
generate实例化
generate
是 Verilog 中的代码生成机制,用于在模块定义中动态、结构性地实例化多个模块、赋值、或条件逻辑。
它本质上是语法上的工具,允许我们在模块中通过 for
、if
等控制结构,自动生成重复或条件性的硬件结构。
generate 可以用来循环实例化模块或条件实例化模块
它常用于:
- 批量实例化模块(如多个乘法器、加法器)
- 条件生成不同的硬件结构
- 构建参数化设计(如可变位宽)
因为重复构建多个实例是结构层面的代码,不是行为逻辑,因此不能用
always
、if
、for
这种行为语句来写,必须用generate
。
基本语法
generate
// 结构代码
endgenerate
generate ... endgenerate
包围的结构会在编译时处理,不会在仿真或综合时运行。
for-generate
用法(循环生成)
genvar i; // 必须用 genvar 声明控制变量
generate
for (i = 0; i < N; i = i + 1) begin : gen_block
// 可实例化模块、连接线、赋值等
end
endgenerate
说明:
genvar
是 generate 专用的变量,不能用于普通 always 块gen_block
是可选的名字,用于结构层次(类似作用域)
if-generate
用法(条件生成)
generate
if (USE_REG == 1) begin : reg_block
// 生成寄存器逻辑
end else begin : comb_block
// 生成组合逻辑
end
endgenerate
常用于根据参数开关生成不同的硬件结构。
case-generate
用法(多分支生成)
generate
case (WIDTH)
8: begin : w8 // 8位逻辑
end
16: begin : w16 // 16位逻辑
end
endcase
endgenerate
根据参数选择不同硬件实现(比如不同位宽的乘法器)。
用途 | 描述 |
---|---|
多模块实例化 | 自动生成多个模块副本 |
参数化设计 | 根据传入的参数动态生成逻辑 |
条件生成 | 控制生成哪种逻辑(例如是否使用寄存器) |
多分支生成 | 根据参数做 case 分支 |
注意事项:
genvar
只能在generate
块中使用generate
块中的变量不会出现在仿真或综合时刻的信号列表中- 实例名称尽量唯一,可用
gen_block[i]
等结构防止冲突 generate
是静态生成语法,不可在运行时决定
【例子】
// 半加器模块的定义
module ha ( input a, b, // 输入 a 和 b
output sum, cout // 输出 sum 和进位 cout
);
assign sum = a ^ b; // sum 为 a 和 b 的异或运算结果
assign cout = a & b; // cout 为 a 和 b 的与运算结果(即进位)
endmodule
// 顶层模块:包含 N 个半加器实例
module my_design
#(parameter N = 4) // 定义参数 N,默认值为 4,表示半加器的个数
(
input [N-1:0] a, b, // 输入 a、b 是 N 位的向量
output [N-1:0] sum, cout // 输出 sum 和 cout 也是 N 位向量
);
// 声明一个用于 generate 的临时变量 i,仿真时不可见
genvar i;
// generate-for 循环,实例化 N 个半加器
generate
for (i = 0; i < N; i = i + 1) begin
// 实例化名为 u0 的半加器模块,第 i 位的输入和输出分别连接
ha u0 (
.a(a[i]), // 输入 a 的第 i 位
.b(b[i]), // 输入 b 的第 i 位
.sum(sum[i]), // 输出 sum 的第 i 位
.cout(cout[i]) // 输出 cout 的第 i 位
);
end
endgenerate
endmodule
运算符
在逻辑运算中 如果操作数不止一位 应将操作数作为一个整体来对待
算术运算符
运算符 | 含义 | 示例 | 说明 |
---|---|---|---|
+ |
加法 | a + b |
|
- |
减法 | a - b |
|
* |
乘法 | a * b |
|
/ |
除法 | a / b |
整数除法,小数部分舍弃 |
% |
取模(余数) | a % b |
✅ 注意:除法/
是取整除,不是浮点数除法!
比较运算符(返回1或 0 或 x)
运算符 | 含义 | 示例 | 结果 |
---|---|---|---|
== |
等于 | a == b |
相等为1,不等为0,不确定为 X |
!= |
不等于 | a != b |
|
=== |
全等于 | a === b |
相等为1,不等为0 |
!== |
不全等于 | a !== b |
|
< |
小于 | a < b |
|
> |
大于 | a > b |
|
<= |
小于等于 | a <= b |
|
>= |
大于等于 | a >= b |
==
和 !=
的特点(普通等于)
- 只看数值是否一致
- 忽略未知值
X
或高阻值Z
- 若任一位为
X
或Z
,比较结果是X
(也就是逻辑不确定)
===
和 !==
的特点(全等运算符)
- 会逐位比较
- 不会忽略
X
和Z
- 只有每一位都完全一致(包括
X/Z
),才返回1
使用建议:
场景 | 建议用 |
---|---|
判断具体数值(如状态) | 使用 == |
判断信号是否含 X 或用于测试用例 |
使用 === |
testbench 中对 X/Z 做比较 |
一定用 === 或 !== |
case语句中的匹配项 | 默认用 == ,可以用 casez/casex 处理模糊匹配 |
位逻辑运算符(逐位操作)
两个不同长度的操作数进行位运算时, 将自动按右端对齐, 位数少的操作数会在高位用 0 补齐
运算符 | 含义 | 示例 | 说明 |
---|---|---|---|
~ |
按位取反 | ~a |
0变1,1变0 |
& |
按位与 | a & b |
两位都1,结果才是1 |
` | ` | 按位或 | `a |
~& |
与非 | ||
`~ | ` | 或非 | |
^ |
按位异或 | a ^ b |
不同为1,相同为0 |
~^ 或 ^~ |
按位同或(XNOR) | a ~^ b |
相同为1,不同为0 |
逻辑运算符(整体为0或1)
运算符 | 含义 | 示例 | 说明 |
---|---|---|---|
! |
逻辑非 | !a |
a 为0则返回1,非0返回0 |
&& |
逻辑与 | a && b |
两个都非0才返回1 |
` | ` |
✅ 区别:
- 位运算是每一位单独算,
- 逻辑运算是整体判断真假(0或非0)。
移位运算符
在移位运算中 将操作数左移或者右移后空位补 0
运算符 | 含义 | 示例 | 说明 |
---|---|---|---|
<< |
左移 | a << 2 |
左移2位,右边补0 |
>> |
右移 | a >> 2 |
右移2位,左边补0(逻辑右移) |
<<< |
算术左移 | a <<< 2 |
保持符号位,左移(和逻辑左移相同) |
>>> |
算术右移 | a >>> 2 |
保持符号位(最高位扩展)右移 |
✅ 一般情况下,普通>>
逻辑右移,>>>
符号扩展右移。
条件运算符(三目运算符)
运算符 | 含义 | 示例 |
---|---|---|
? : |
条件判断 | d = sel ? a : b; |
意思是:
- 如果
sel==1
,那么d=a
- 如果
sel==0
,那么d=b
拼接与重复运算符
拼接运算符 { , }
- 把多个信号拼接成一个大的信号。
例子:
{a, b} // a在高位,b在低位
如果:
a = 4'b1100;
b = 4'b0011;
{a,b} = 8'b11000011;
重复运算符 {N{表达式}}
- 把表达式复制N次。
例子:
{4{1'b1}} // 结果是 4'b1111
优先级
顺序(高到低):
1. () 小括号
2. ~ ! 取反(位、逻辑)
3. * / % 乘除取模
4. + - 加减
5. << >> <<< >>> 移位
6. < <= > >= 比较
7. == != 等于、不等
8. & 位与
9. ^ ~^ 位异或/同或
10. | 位或
11. && 逻辑与
12. || 逻辑或
13. ?: 条件选择
✅ 写复杂表达式时,一定多用小括号 ()
,不要只靠优先级,避免出错。
分类 | 常用运算符 |
---|---|
算术运算 | + - * / % |
比较运算 | == != < > <= >= |
位运算 | `~ & |
逻辑运算 | `! && |
移位运算 | << >> <<< >>> |
条件运算 | ? : |
拼接/重复 | { , } , {N{}} |
信号赋值
assign 连续赋值(只给wire用)
- 用来连接信号或者表达式。
- 等式左边必须是一个标量或者线性向量,而不能是寄存器类型
- 等式右边的类型没有要求,等式右边的值一旦发生变化,就会立刻重新计算并同时赋值给左侧
例子:
assign y = a & b;
意思:y
始终是 a
和 b
按位与的结果。
分类 | 特点 | 常用赋值方式 |
---|---|---|
wire | 连线,不存储数据 | assign |
reg | 存储单元,可在always赋值 | always块 |
assign | 连续赋值,只能给wire | assign y = a & b; |
always块 | 过程赋值,时序或组合逻辑 | always @(…) begin … end |
wire初始化写法
这段:
wire c = 1'b0;
实际上是简写,它等价于:
wire c;
assign c = 1'b0;
就是说:
- 声明了一根名叫
c
的线。 - 然后用
assign
把1'b0
这个常量连上去。 - 在综合和仿真里,
c
就相当于一直是0。
常量表示法
N'bX
格式- N是位宽(多少位)
'b
是进制(b是binary,d是decimal,h是hexadecimal)- X是值
例子:
4'b1010 // 4位宽,二进制1010 8'd255 // 8位宽,十进制255 16'h1A2B // 16位宽,16进制1A2B
过程赋值(时序控制)
阻塞赋值(=):语句顺序执行。下一条语句执行前,当前语句一定会执行完毕。
【例】:
always @(posedge clk) begin b=a c=b end
非阻塞赋值(<=):语句并行执行,常用于时序逻辑(如D触发器)。下一条语句和当前语句是同时进行的。
【例】:
always @(posedge clk)begin b <= a; c <= b; end
不要在同一个
always
块中混用=
和<=
对同一个变量赋值
类型 | 运算符 | 执行顺序 | 典型作用域 | 用法建议 |
---|---|---|---|---|
阻塞赋值 | = |
顺序执行 | initial 、always @* |
组合逻辑、测试初始化等 |
非阻塞赋值 | <= |
并发执行 | always @(posedge ...) |
时序逻辑 |
always块(过程赋值)
过程赋值 是在 initial 或 always 语句块里的赋值 主要用于对寄存器(rag)类型变量进行赋值。
寄存器变量在被赋值后,其值将保持不变,直到重新被赋予新值
- 描述时序逻辑或组合逻辑。
- 过程赋值可以是**阻塞赋值 **或者 非阻塞赋值 。
always 语句块通常带有触发条件
语句块中的语句会重复执行,一个变量不能在多个 always 块中被赋值。
在 always 块中被赋值的只能是 register 型变量
always 语句块即可以用来实现组合逻辑也可以用来实现时序逻辑
基础语法:
always @(sensitivity_list)
begin
// 一系列语句
end
其中 sensitivity_list
是敏感列表,表示当列表中的信号发生变化时触发 always
块。
always
块的分类
根据 sensitivity_list
内容不同,分为以下几种类型:
① 组合逻辑(组合逻辑电路建模)
always @(*) // 或 always @(a or b or sel)
begin
// 组合逻辑
end
等价于 assign
写的逻辑,常用于使用 if
、case
等构建复杂组合逻辑。
🔸 示例:多路选择器(MUX)
module mux_2to1 (
input wire a, b,
input wire sel,
output reg y
);
always @(*) begin
if (sel)
y = b;
else
y = a;
end
endmodule
② 时序逻辑(用于寄存器建模)
always @(posedge clk)
begin
// 时序逻辑(寄存器)
end
或
always @(posedge clk or negedge rst_n)
begin
if (!rst_n)
q <= 0;
else
q <= d;
end
🔸 示例:D触发器
module dff (
input clk,
input rst_n,
input d,
output reg q
);
always @(posedge clk or negedge rst_n) begin
if (!rst_n)
q <= 1'b0;
else
q <= d;
end
endmodule
③ 时序逻辑 + 状态机
🔸 示例:简单 FSM(两个状态,翻转输出)
module fsm (
input clk, rst_n,
input in,
output reg out
);
reg state;
always @(posedge clk or negedge rst_n) begin
if (!rst_n)
state <= 1'b0;
else
state <= ~state;
end
always @(*) begin
if (state)
out = in;
else
out = ~in;
end
endmodule
项目 | 内容 |
---|---|
@(*) |
自动推导敏感信号,建议用于组合逻辑 |
=(阻塞赋值) |
常用于组合逻辑 |
<=(非阻塞赋值) |
常用于时序逻辑 |
always 不是函数 |
它是对“硬件电路行为”的一种模拟 |
多个 always 可同时使用 |
同时描述不同逻辑块或功能单元 |
类型 | 敏感列表 | 用途 | 赋值类型 |
---|---|---|---|
组合逻辑 | @(*) |
多路选择器、加法器等 | = |
时序逻辑 | @(posedge clk) 或 @(posedge clk or negedge rst) |
寄存器、计数器、状态机 | <= |
状态机建模 | 多个 always 配合使用 | 控制逻辑、序列识别 | 混合 |
另外,不带有敏感信号的 always 语句块会一直执行,因此可用于仿真时钟信号生成
always #10 clk = ~clk;
initial块(只在仿真用)
- 只在仿真(simulation)时使用。沿时间轴只执行一次。
- 主要用途是在仿真的初级阶段对各变量进行初始化
- 在电路上不综合成硬件。
【例】:初始时,a赋0,b赋1。
initial begin
a = 0;
b = 1;
end
组合逻辑 vs 时序逻辑
组合逻辑:输出只依赖输入,没有存储状态
assign y = a & b;
或者
always @(a or b) begin y = a & b; end
时序逻辑:输出依赖时钟(clk),有状态记忆
always @(posedge clk) begin q <= d; end
关键语法
模块定义
module module_name (input a, input b, output y); // 逻辑 endmodule
连续赋值
assign y = a & b;
过程块
always块(常用于描述时序逻辑和组合逻辑)
always @(posedge clk) begin q <= d; end
initial块(仅用于仿真初始化)
initial begin a = 0; b = 1; end
**设计层次
结构建模(Structural Modeling)
像搭积木一样,用已有的模块(门级、子模块)实例化并连接,描述硬件结构。
特点:
- 接近硬件电路图结构
- 每个模块都要明确实例化
- 比如用
and
、or
、xor
、not
或用户定义模块来搭建电路
示例(半加器结构建模):
module ha (input a, b, output sum, cout);
wire xor_out, and_out;
xor u1 (xor_out, a, b); // 门级建模
and u2 (and_out, a, b);
assign sum = xor_out;
assign cout = and_out;
endmodule
也可以这样模块化使用:
module top;
wire s, c;
ha u0 (.a(1'b1), .b(1'b0), .sum(s), .cout(c)); // 实例化 half-adder
endmodule
📌 优点:
- 电路结构直观,适合底层硬件构建
- 可以重用子模块
⚠️ 缺点:
- 不适合复杂逻辑描述
- 可读性差,模块多时臃肿
数据流建模(Dataflow Modeling)
数据流建模 方式要比结构化描述方式抽象级别要高一些,因为它不在需要清因为它不在需要清晰地刻画出具体的数字电路架构
用连续赋值语句(assign
)表示信号间的数据传递关系,相当于表达电路逻辑函数。
特点:
- 更抽象,强调“数据如何流动”
- 使用
assign
+ 表达式建模逻辑 - 属于组合逻辑
示例(半加器的数据流建模):
module ha (input a, b, output sum, cout);
assign sum = a ^ b;
assign cout = a & b;
endmodule
📌 优点:
- 简洁易写,适合组合逻辑
- 比结构更抽象,便于阅读
⚠️ 缺点:
- 不适用于时序逻辑(需要时钟的寄存器等)
- 纯数据流的描述方式只适用于小规模的电路设计
行为建模(Behavioral Modeling)
行为描述 是对设计电路的逻辑功能的描述 并不关心设计电路使用哪些元件以及这些元件之间的连接关系(更接近高级语言)
属于高层次的描述方法 类似高级语言可以使用 控制流 和 循环语句 等功能
描述电路行为,比如if-else、case等。
定义:
描述的是“行为”或“功能”,用**过程语句(always
、initial
)**来控制电路行为。更接近写软件代码的风格。
特点:
- 使用
always
块来表示组合/时序逻辑 - 可使用
if
、case
、for
等语句 - 分为组合逻辑建模和时序逻辑建模
示例(时序逻辑建模 - 寄存器):
module dff(input clk, rst, d, output reg q);
always @(posedge clk or negedge rst) begin
if (!rst)
q <= 1'b0;
else
q <= d;
end
endmodule
📌 优点:
- 抽象度最高,适合复杂控制逻辑(如 FSM)
- 逻辑简洁,代码量少
⚠️ 缺点:
- 与硬件结构关系不直接,难以优化资源
- 更依赖综合工具识别其意图
常用语句
if语句
always @(a or b) begin if (a) y = 1; else y = 0; end
case语句
always @(sel or a or b) begin case(sel) 0: y = a; 1: y = b; default: y = 0; endcase end
for循环(通常只用于仿真或者可综合的有限循环)
1.给控制循环次数的变量赋初值
2.判定循环执行条件, 若为假则跳出循环;若为真, 执行指定的语句后, 转到第 3 步
3.修改循环变量的值 返回第 2 步integer i; always @(posedge clk) begin for (i = 0; i < 8; i = i + 1) mem[i] <= 0; end
while循环
执行一条语句直到某个条件不满足 。
首先判断循环执行条件表达式是否为真:
若为真 则执行后面的语句或语句块 直到条件表达式不为真;
若不为真 则其后的语句一次也不被执行
repeat 语句
forever语句
task语句
function语句
其他常见概念
参数(parameter):定义常量或可配置的参数。
parameter WIDTH = 8; wire [WIDTH-1:0] data;
生成块(generate):用于生成结构化重复硬件(如阵列结构)。
genvar i; generate for (i = 0; i < 4; i = i + 1) begin my_module u (.in(in[i]), .out(out[i])); end endgenerate
仿真专用语句
$display
:打印信息。$monitor
:实时监视信号变化。$finish
:仿真结束。
测试平台(Testbench)
- Testbench是验证设计模块正确性的仿真代码,不综合进电路。
- 包含:
initial
块生成激励(stimulus)- 实例化待测模块(DUT)
- 观察输出(可加断言、检查)