07. 条件、case、循环与 generate
Verilog 的 if/case/for 看起来像软件控制流,但综合时它们通常选择或复制硬件,不是让 FPGA 里的处理器执行循环。
if/else:有优先级的选择
跳转到“if/else:有优先级的选择”always @(*) begin if (emergency) mode = 2'b11; else if (manual) mode = 2'b10; else if (automatic) mode = 2'b01; else mode = 2'b00;end如果多个条件同时成立,靠前条件优先。代码可能形成串联的多路选择器。优先级是需求时,这很合适;若条件本应互斥,要通过接口约束和验证保证。
case:按一个表达式选择
跳转到“case:按一个表达式选择”always @(*) begin y = 8'b0; case (opcode) 3'b000: y = a + b; 3'b001: y = a - b; 3'b010: y = a & b; 3'b011: y = a | b; 3'b100: y = a ^ b; default: y = 8'b0; endcaseend普通 case 按四态逐位匹配。default 一方面处理未列出的编码,另一方面帮助组合逻辑覆盖所有赋值路径。
casez 与 casex
跳转到“casez 与 casex”casez把z和?视为无关位;casex还会把x视为无关位。
优先编码器可用 casez 表示通配:
always @(*) begin valid = 1'b1; code = 2'b00; casez (request) 4'b1???: code = 2'b11; 4'b01??: code = 2'b10; 4'b001?: code = 2'b01; 4'b0001: code = 2'b00; default: valid = 1'b0; endcaseend设计 RTL 中慎用 casex:真实错误产生的 x 可能被当成通配而隐藏。优先选择普通 case,确需通配时优先 casez 并配合验证。
for 过程循环:展开重复计算
跳转到“for 过程循环:展开重复计算”integer i;reg parity;
always @(*) begin parity = 1'b0; for (i = 0; i < 8; i = i + 1) parity = parity ^ data[i];end综合器通常把固定次数循环展开成硬件。它不是一个异或门连续运行 8 次,而是形成等效的异或网络。循环边界应在综合时可确定。
再看逐位赋值:
integer bit_index;always @(*) begin for (bit_index = 0; bit_index < WIDTH; bit_index = bit_index + 1) inverted[bit_index] = ~data[bit_index];end这会生成 WIDTH 份并行逻辑。
while/repeat/forever
跳转到“while/repeat/forever”这些循环在 testbench 中很有用:
repeat (10) begin #20 stimulus = stimulus + 1'b1;end
forever #5 clk = ~clk;用于综合时要非常谨慎。无法静态确定次数的循环不能直接变成有限硬件;需要多周期迭代时,应设计计数器和状态机。
generate:按参数生成结构
跳转到“generate:按参数生成结构”generate 在展开模块层次时决定要创建哪些实例或连续结构。
module bitwise_and #( parameter WIDTH = 8) ( input wire [WIDTH-1:0] a, input wire [WIDTH-1:0] b, output wire [WIDTH-1:0] y);
genvar i; generate for (i = 0; i < WIDTH; i = i + 1) begin : gen_and assign y[i] = a[i] & b[i]; end endgenerate
endmodulegen_and 给每次展开的层次命名,便于报错、约束和波形定位,如 gen_and[3]。
条件生成
跳转到“条件生成”generate if (USE_REGISTER) begin : gen_registered always @(posedge clk) data_out <= data_in; end else begin : gen_bypass always @(*) data_out = data_in; endendgenerate这里的 if 在编译/展开时根据参数选择硬件,不是运行时 MUX。参数改变后,电路结构才改变。
注意:示例中同一输出在不同互斥生成分支中使用不同过程类型,真实工程应确保端口类型和工具兼容性。更清楚的做法往往是用内部 wire/reg 分别承接,再统一连接输出。
过程 for 与 generate for 的区别
跳转到“过程 for 与 generate for 的区别”| 维度 | 过程 for | generate for |
|---|---|---|
| 所在位置 | always/initial 内 | 模块级 |
| 循环变量 | integer 等过程变量 | genvar |
| 常见用途 | 重复赋值/计算 | 重复实例/结构 |
| 是否创建命名层次 | 通常不创建 | 会创建生成块层次 |
不要用循环表达“等待”
跳转到“不要用循环表达“等待””下面的软件式想法不适合普通可综合 RTL:
while (!done) begin // 想在这里等待硬件完成end硬件等待多个周期应写成状态:空闲时启动,运行状态等待 done,完成状态输出结果。每个时钟沿前进一步。
练习
跳转到“练习”- 用
case写 2 位控制码的 4 路 MUX,并覆盖default。 - 用过程
for计算 8 位输入中有多少个 1;思考加法树需要多宽。 - 用 generate 实例化 4 个相同的一位全加器,形成 4 位脉动进位加法器。
- 解释运行时
if (sel)和生成时if (USE_FEATURE)的硬件区别。
上一篇:时序逻辑 · 返回目录 · 下一篇:函数、任务与仿真