跳转到内容
新建笔记

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;
endcase
end

普通 case 按四态逐位匹配。default 一方面处理未列出的编码,另一方面帮助组合逻辑覆盖所有赋值路径。

  • 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;
endcase
end

设计 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 份并行逻辑。

这些循环在 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
endmodule

gen_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;
end
endgenerate

这里的 if 在编译/展开时根据参数选择硬件,不是运行时 MUX。参数改变后,电路结构才改变。

注意:示例中同一输出在不同互斥生成分支中使用不同过程类型,真实工程应确保端口类型和工具兼容性。更清楚的做法往往是用内部 wire/reg 分别承接,再统一连接输出。

过程 for 与 generate for 的区别

跳转到“过程 for 与 generate for 的区别”
维度过程 forgenerate for
所在位置always/initial 内模块级
循环变量integer 等过程变量genvar
常见用途重复赋值/计算重复实例/结构
是否创建命名层次通常不创建会创建生成块层次

不要用循环表达“等待”

跳转到“不要用循环表达“等待””

下面的软件式想法不适合普通可综合 RTL:

while (!done) begin
// 想在这里等待硬件完成
end

硬件等待多个周期应写成状态:空闲时启动,运行状态等待 done,完成状态输出结果。每个时钟沿前进一步。

  1. 用 case 写 2 位控制码的 4 路 MUX,并覆盖 default。
  2. 用过程 for 计算 8 位输入中有多少个 1;思考加法树需要多宽。
  3. 用 generate 实例化 4 个相同的一位全加器,形成 4 位脉动进位加法器。
  4. 解释运行时 if (sel) 和生成时 if (USE_FEATURE) 的硬件区别。

上一篇:时序逻辑 · 返回目录 · 下一篇:函数、任务与仿真