[Verilog HDL]第五章 条件语句、循环语句、块语句、生成语句
本章全部语句属于行为级描述语句,仅能放置在initial、always过程块内部,模块顶层不能直接书写。本章语句分为两大类:可综合硬件语句(能生成真实电路)、仿真专用语句(仅用于 Testbench 测试,无法综合)。
学习目标:熟练使用 if/case 完成多路控制;区分仿真循环与硬件生成循环;掌握顺序 / 并行块仿真时序差异。
一、5.1 if-else 条件语句 ★
1. if 语句三种标准语法结构
1.1 格式 1:单 if 语句(无 else 分支)
适用场景:仅需要在特定条件下执行操作,其余状态无赋值
always @(*) begin if(en) out = in; end缺陷:组合逻辑下缺少 else 分支会综合生成锁存器,时序逻辑无此风险。
1.2 格式 2:if-else 双分支语句
条件分为真假两路,完整覆盖所有情况,组合逻辑推荐使用,不会产生锁存器。
always @(*) begin if(sel) out = a; else out = b; end1.3 格式 3:if-else if 多分支优先级语句
多层判断自带优先级,从上至下匹配,上层条件优先执行。
always @(*) begin if(addr == 0) data = 8'd1; else if(addr == 1) data = 8'd2; else data = 8'd0; end2. 书写规范细节
- 单条执行语句可省略
begin-end;多条语句必须使用begin-end包裹复合块; - 判断表达式支持逻辑运算
&& || !、比较运算== != > < >= <=; - 简写写法:
if(!rst_n)等价于if(rst_n == 0)。
3. else 就近匹配规则(易错点)
else 永远匹配离它最近、未配对的 if,与缩进无关。
错误示例:
if(a) if(b) y=1; else y=0; // 匹配内层if(b),而非外层if(a)解决方法:多层嵌套统一添加begin-end明确代码块边界。
4. 组合逻辑锁存器产生原理
组合逻辑always @(*)中,若变量在部分 if 分支中未赋值,综合工具会生成锁存器保存旧值,导致电路功能异常。 规避方案:
- 完整书写 else 分支,给变量赋予默认值
- 在块开头统一给输出赋默认值。
5. 硬件电路特性
多层 if 语句会生成带优先级的多路选择电路,适合优先级编码器、中断判断电路。\
二、case 多路分支语句 ★★
1. case 基础完整语法
case(控制信号) 分支1: 执行语句; 分支2: 执行语句; default: 默认语句; // 必须添加,防止锁存器 endcase- 所有分支取值必须互斥,禁止出现重叠条件;
- default 分支作用:覆盖未枚举的 0/1/x/z 状态,消除锁存器。
2. case /casez/casex 匹配规则★★
case:严格逐位匹配,0 只能匹配 0、1 匹配 1、x 匹配 x、z 匹配 z。casez:忽略高阻 z,?作为无关位通配符,仅严格匹配 0/1;常用于位屏蔽译码。casex:同时忽略 x、z 不定态,仅识别 0/1,多用于仿真判断未知输入。
3. 硬件特性
case 所有分支优先级完全平等,无先后顺序,适合多路选择器。
4. 实操代码示例
3 位二进制译码器
reg [7:0] y; always @(*) begin case(addr) 3'b000: y = 8'b00000001; 3'b001: y = 8'b00000010; 3'b010: y = 8'b00000100; default: y = 8'b00000000; endcase end三、if 与 case 语句核心对比总结
| 对比项 | if-else | case |
|---|---|---|
| 优先级 | 自上而下,上层优先 | 所有分支平等无优先级 |
| 适用电路 | 优先级控制、中断判断 | 多路选择器、译码器 |
| 不定态处理 | 仅判断 0/1,无法屏蔽 x/z | 支持 casez/casex 处理无关位 |
| 锁存器风险 | 漏 else 产生锁存器 | 缺少 default 产生锁存器 |
四、多路分支电路实操案例
四选一多路选择器,完整综合代码
module mux4( input [1:0] sel, input a,b,c,d, output reg out ); always @(*) begin case(sel) 2'b00: out = a; 2'b01: out = b; 2'b10: out = c; 2'b11: out = d; default: out = 1'b0; // 规避锁存器 endcase end endmodule关键知识点
- 敏感列表
@(*)自动包含 sel、a、b、c、d 所有输入。 - default 分支不可省略,否则综合出现锁存器。
- 综合后生成纯组合逻辑,无触发器。
五、 循环语句 ★
在Verilog HDL中存在着4种类型的循环语句,用来控制执行语句的执行次数。
统一约束:forever/repeat/while/for四类循环仅能写在 initial 仿真块。时序 / 组合 always 块中使用会报综合错误。
1. forever 语句
连续的执行语句。forever循环语句常用于产生周期性波形,用来作为仿真测试信号。
仿真示例:
initial begin clk = 0; forever #10 clk = ~clk; // 周期20ns时钟 end2. repeat语句
连续执行一条语句n次。repeat语句中,其表达式通常为常量表达式。适合存储器初始化、批量激励赋值。
initial begin repeat(16) begin addr = addr + 1; #5; end end3. while语句
执行一条语句直到某个条件不满足。先判断条件,条件为真才执行循环体;初始条件不成立则一次不运行。
initial begin cnt = 0; while(cnt < 8) begin cnt = cnt + 1; end end4. for语句
4.1 一般形式
for(初始化;循环条件;更新语句)
执行语句;
4.2 执行过程
- 先给控制循环次数的变量赋初值。
- 判定控制循环的表达式的值,如为假,则跳出循环语句;如为真,则执行指定的语句后,转到第三步。
- 执行一条赋值语句来修正控制循环变量次数的变量值,然后返回第二步。
initial begin for(i=0;i<16;i=i+1) begin mem[i] = 0; end end六、顺序块与并行块 ★★
块语句作用:将多条语句打包为整体,统一管理执行时序,分为begin-end顺序块、fork-join并行块。
1. 顺序块 begin-end
- 内部语句按书写顺序逐条执行,延时依次累加。
- 无数据竞争,组合 / 时序逻辑块默认使用。
initial begin #10 a=1; #20 b=0; // 总延时30ns执行b赋值 end2. 并行块 fork-join
- 块内所有语句同一时刻同步启动,延时互不叠加。
- 风险:同一时刻修改同一寄存器,产生数据竞争,仿真结果随机。
initial fork #10 a=1; #20 b=0; // a在10ns执行,b在20ns执行,互不干扰 join3.特性
- 嵌套块:顺序块、并行块可互相嵌套;
- 命名块:给块自定义名称,内部可定义局部变量;
- disable 关键字:强制终止指定命名块循环,用于仿真跳出循环。
七、生成块 generate ★★
唯一可综合的循环结构,编译阶段批量生成重复硬件电路,用于参数化总线、多位加法器、阵列电路;仅能写在模块顶层,禁止放入 always/initial 内部。
1. generate 通用语法限制
- generate 变量仅编译阶段有效,仿真运行不存在;
- 内部可例化模块、门原语、assign 连续赋值,禁止时序延时
#; - 必须包裹在
generate-endgenerate关键字之间。
2. 循环生成语句 generate for
批量例化相同子模块,实现 N 位硬件阵列。
示例:4 位逐进位加法器批量生成单元
genvar i; generate for(i=0;i<4;i=i+1) begin full_adder u_adder(.a(a[i]),.b(b[i]),.cin(cin[i]),.sum(sum[i]),.cout(cout[i])); end endgenerate3. 条件生成语句
generate if
根据参数数值,选择性生成不同硬件结构。
应用场景:根据位宽参数选择慢速 / 高速加法器。
4. case 生成语句
generate case
多分支参数匹配,根据参数切换硬件架构。
八、综合实操案例
案例 1:四位同步计数器(时序逻辑 + if 语句)
module cnt4( input clk,rst_n, output reg [3:0] cnt ); always @(posedge clk) begin if(!rst_n) cnt <= 4'd0; // 同步复位 else cnt <= cnt + 1'b1; end endmodule知识点:时序逻辑使用非阻塞赋值<=;if 实现同步复位,综合生成 4 组 D 触发器。
九、核心总结
- 语句分层:可综合语句(if/case/generate)、仿真专用循环(forever/for/repeat/while);
- 锁存器两大来源:组合逻辑 if 无 else、case 无 default;
- 电路选型口诀:优先级电路用 if,平等多路选择用 case;
- 循环两类区分:仿真 for(运行时循环)、generate for(编译生成硬件);
- 仿真块区分:begin 顺序执行、fork 并行执行,并行块易产生数据竞争。
十、思考题
- 组合逻辑 if/case 分支不全为什么会生成锁存器?如何规避?
- case、casez、casex 三者匹配规则有什么区别,分别适用什么场景?
- fork 并行块数据竞争如何产生,有哪些解决办法?
- generate 循环和仿真 for 循环本质区别是什么?能否互相替换?
- forever、repeat、while、for 四类仿真循环各自适用场景?
十一、重难点汇总
- 基础:if 就近匹配、锁存器产生条件、多路选择器、同步计数器;
- 重点:casez/casex 不定态匹配、fork 并行时序竞争、generate 编译展开原理;
- 高频易错:仿真循环用于综合报错、组合时序赋值符号混用、缺少 default/else 分支。