模块、参数与 generate
命名端口连接
跳转到“命名端口连接”adder #(.WIDTH(16)) u_adder ( .a (a[15:0]), .b (b[15:0]), .sum (sum[16:0]));.a(a[15:0]) 左侧是子模块端口,右侧是当前层级信号。工程代码优先使用命名连接:端口顺序变化时更安全,也更容易审查位宽。
参数化
跳转到“参数化”module counter #( parameter int unsigned WIDTH = 16) ( input logic clk, input logic rst, input logic enable, output logic [WIDTH-1:0] count); always_ff @(posedge clk) begin if (rst) begin count <= '0; end else if (enable) begin count <= count + 1'b1; end endendmodule参数在 elaboration 前确定,适合位宽、深度和功能选项。运行时变化的配置应使用输入端口或寄存器,而不是参数。
generate for
跳转到“generate for”for (genvar i = 0; i < LANES; i++) begin : g_lane lane_rx u_lane_rx ( .clk (clk), .din (serial_in[i]), .data (lane_data[i]) );end这会生成 LANES 个并行实例。命名块 g_lane 形成稳定层次,例如 g_lane[3].u_lane_rx,方便约束、波形和调试定位。
generate if
跳转到“generate if”if (USE_PIPELINE) begin : g_pipe always_ff @(posedge clk) begin y <= a + b; endend else begin : g_comb always_comb begin y = a + b; endend条件必须在 elaboration 时可确定。两条分支不会同时生成。
结构设计原则
跳转到“结构设计原则”- 模块边界围绕稳定职责和时钟域,而不是每几行代码拆一个模块。
- 端口显式携带方向、位宽、符号和时钟域含义。
- 数据与
valid/ready/last等控制信号一起封装和流水。 - 不在层级中到处使用跨层引用;调试信号通过端口或 bind/ILA 处理。
- 对参数范围做 elaboration-time 检查,避免
WIDTH=0等非法组合。