3.2 if、case、loop 三种顺序骨架


3.2 if、case、loop 三种顺序骨架

本节摘要:顺序语句只存在于进程与子程序内部。本节对比 if、case、loop 三种控制结构的语法要点与综合产物,重点解剖"不完整赋值生成锁存器"这一新手最大雷区,并给出循环语句在可综合代码中的正确用法。

写在前面:顺序的舞台只有进程

先把边界说死:if、case、loop、变量赋值这些顺序语句只能写在进程、函数或过程里。写在架构语句区会直接编译报错。进程内部是顺序世界——从上到下执行,直到进程挂起。三种骨架解决三类问题:if 做优先级选择,case 做并列选择,loop 做重复结构。选错骨架,轻则浪费资源,重则综合出意料之外的电路。

if 链:优先级的化身

process (r, g, b, keys) begin if keys(3) = '1' then mux_out <= r; -- keys(3) 优先级最高 elsif keys(2) = '1' then mux_out <= g; elsif keys(1) = '1' then mux_out <= b; else mux_out <= '0'; end if; end process;

综合器把 if-elsif 链翻译成级联的优先多路器:keys(3) 的判断在最外层,物理路径最短;keys(1) 的信号要穿过三级选择电路。这就是"if 链有优先级"的物理含义。四个分支各有先后,恰好是中断仲裁、总线仲裁这类场景要的语义——if 链是自然选择。

case:并列选择与并行多路器

同样的选择,case 版本把所有分支摆在同一层:

process (sel) begin case sel is when "00" => mux_out <= r; when "01" => mux_out <= g; when "10" => mux_out <= b; when others => mux_out <= '0'; -- 必须兜底 end case; end process;

综合结果是一颗并行多路器,所有分支延迟接近,没有级联。两条铁律:case 的选择值必须覆盖所有取值(用 when others 兜底,std_logic_vector 的九值特性决定了穷举二进制值也不算覆盖完整);分支之间不能重叠。经验法则:分支条件互相独立、无轻重之分,用 case——资源更省、路径更平衡;条件本身是"先到先得"的优先关系,用 if。

雷区解剖:不完整赋值 = 锁存器

把上面 case 的 when others 分支删掉,综合器不会报错,而是默默干一件事:生成一个锁存器,用来记住"sel 取到未列值时 mux_out 该保持什么"。同理,if 没写 else、组合进程里某条路径没给输出赋值,都会催生锁存器。锁存器在 FPGA 里是坏消息:时序分析困难、极易产生毛刺。识别方法:综合报告里搜 "latch" 关键字;根治方法:组合进程的每条执行路径都必须给所有输出赋值。防呆写法是进程开头先给默认值:

process (sel, r, g, b) begin mux_out <= '0'; -- 默认值兜底 case sel is when "00" => mux_out <= r; when "01" => mux_out <= g; when "10" => mux_out <= b; when others => null; -- 保持默认值 end case; end process;

loop:为重复结构而生

可综合的循环有一条铁律:循环次数必须是编译期可确定的。for 循环天然满足;while 循环若依赖信号值,综合器通常拒绝。循环的正确用途是生成重复的运算结构,比如把八位输入逐位异或到一起生成奇偶位:

process (d) variable par : std_logic; begin par := '0'; for i in d'range loop -- d'range 自适应位宽 par := par xor d(i); -- 变量即时生效,循环内累积 end loop; parity <= par; -- 循环结束一次性输出 end process;

这个例子还示范了变量的价值:par:= 赋值即刻可读,循环体内每次异或都基于最新值——这正是循环累积运算需要的语义。如果换成信号,要等 delta 才生效,循环八次全是旧值。变量细节下一节展开,这里先记住:循环内累积用变量,循环结束赋给信号

loop 与 exit、next 配合能提前终止或跳过某轮,但可综合代码里克制使用——综合器会完全展开循环,写成"八份异或门树",写再花哨的循环控制也只是改变这棵树的形状。

骨架 语义 综合产物 典型场景
if-elsif 链 优先级判断 级联多路器 仲裁、抢答
case 并列选择 并行多路器 译码、状态分支
for loop 重复展开 重复运算结构 奇偶、逐位处理
while loop 条件重复 多数不可综合 仅仿真脚本用

⚠️ 常见坑:case 分支里忘写 when others,以为二进制穷举就完事——std_logic_vector 每位有九种值,穷举的是 4 的幂级组合,编译器照样认为覆盖不全。九值系统里 when others 永远省不得。

一个反例加固判断

看一段"骨架选错"的代码:四种互斥的运算档位,分支之间毫无优先关系,却写成了 if 链:

-- 不推荐:无优先语义却用了优先结构 if op = "00" then y <= a + b; elsif op = "01" then y <= a - b; elsif op = "10" then y <= a and b; else y <= a or b; end if;

功能完全正确,但综合出的级联结构让"或"档的路径平白多穿两级选择器——四个档位的延迟不均衡,最慢档决定整个模块的时序上限。改成 case 后四档等长,WNS 直接改观。反过来,抢占式仲裁器硬写成 case(把优先关系塞进分支条件表达式里)也能跑,但代码把优先级藏进布尔表达式,可读性崩坏。骨架选择就是把自己脑中的语义结构(有先来后到吗?)显式告诉读代码的人与综合器,语义与结构对齐,两边都受益。

💡 关键直觉:写选择结构前先问"分支之间有没有高下之分"。有,if 链把最优先的写最前;没有,case 让它们平起平坐。这个决定直接决定了电路的形状与时序。

本节要点回顾

  • if 链带优先级:级联多路器,先写的分支路径最短;
  • case 并列:并行多路器,分支均衡,必须 when others 兜底;
  • 不完整赋值生锁存器:组合进程每条路径都要覆盖全部输出,默认值法最防呆;
  • 循环要编译期定次数:for 可综合,while 基本留给仿真;
  • 循环内累积用变量:= 即时生效,退出循环再赋信号。

作者与出处
原作者: 灏天文库
来源:灏天文库
整理: 灏天文库整理
由灏天文库平台收录,内容或由平台用户上传,仅供学习交流
发布者: 作者: 灏天文库 转发
评论区 (0)
U