csa_tree
我们开始分析 csa_tree.sv
的前 20 行,这是一个递归式 Carry-Save Adder Tree 模块,用于将任意数量的同位宽输入压缩为两组:sum 和 carry。
🧾 第 1 行:功能注释
// Recursive implementation of carry-save-adder (CSA) tree, to compress inputs with any size into sum and carry
📘 功能说明:该模块能接受任意数量的操作数,并递归地使用 CSA(或 Wallace 树)压缩,最终生成 sum_o
和 carry_o
。
🔧 第 2–5 行:模块定义与参数
module csa_tree #(
parameter int unsigned N = 8, // size of inputs
parameter int unsigned WIDTH_I = 8, // bit-width of inputs
parameter int unsigned WIDTH_O = WIDTH_I + pdpu_pkg::clog2(N) // bit-width of outputs
)(
参数 | 含义 |
---|---|
N |
输入个数(例如部分积数) |
WIDTH_I |
每个输入的位宽 |
WIDTH_O |
输出位宽,最大进位深度为 log2(N) |
🔌 第 6–9 行:端口定义
input logic [N-1:0][WIDTH_I-1:0] operands_i,
output logic [WIDTH_O-1:0] sum_o,
output logic [WIDTH_O-1:0] carry_o
);
名称 | 说明 |
---|---|
operands_i |
输入的 N 个操作数,每个为 WIDTH_I |
sum_o |
最终压缩结果的 sum 位 |
carry_o |
最终压缩结果的 carry 位 |
🔁 第 10–12 行:递归拆分数量
localparam int unsigned N_A = N/2;
localparam int unsigned N_B = N - N_A;
📘 将 N 拆分为两部分 A 和 B,以便递归压缩。
🔂 第 14–19 行:递归压缩起始条件处理
generate
if (N==1) begin
assign sum_o = operands_i[0];
assign carry_o = '0;
end
else if(N==2) begin
assign sum_o = operands_i[0];
📌 基本情况:
- 如果只有一个输入 ⇒ 输出为其本身
- 如果两个输入 ⇒ 直接传给 sum(carry 置 0)
是否继续分析递归压缩情况(N ≥ 3)时如何使用 compressor_3to2
进行递归构建?
我们继续分析 csa_tree.sv
中第 41–60 行,主要是当输入数量 N > 4
时,递归拆分输入数组并分别调用自身 的过程:
🔁 行 43–44:定义局部输入拆分数组
logic [N_A-1:0][WIDTH_I-1:0] operands_i_A;
logic [N_B-1:0][WIDTH_I-1:0] operands_i_B;
📌 将原始输入 operands_i
分为两段:前半 A
和后半 B
,用于递归处理。
🔢 行 45–48:局部输出信号定义
logic [WIDTH_O-1:0] sum_o_A;
logic [WIDTH_O-1:0] sum_o_B;
logic [WIDTH_O-1:0] carry_o_A;
logic [WIDTH_O-1:0] carry_o_B;
📘 两个子树的输出 sum
和 carry
。
🔃 行 50–51:输入拆分连接
assign operands_i_A = operands_i[N_A-1:0];
assign operands_i_B = operands_i[N-1:N_A];
将 operands_i
切分为前 N_A 和后 N_B 两段输入。
🔁 行 53–59:第一个递归子树调用
csa_tree #(
.N(N_A),
.WIDTH_I(WIDTH_I),
.WIDTH_O(WIDTH_O)
) ua_csa_tree(
.operands_i(operands_i_A),
📌 调用自身,对 A
部分的操作数进行压缩。
接下来应会连接该子树的输出,并对 B
段重复相同过程。
是否继续分析第二个递归子树和最终组合输出的逻辑?
我们继续分析 csa_tree.sv
中的第 61–80 行,这部分完成了第二个子树的递归调用,并将两个子树的 sum
和 carry
输出组合压缩为最终输出:
🔁 行 61–63:第一个子树输出连接完成
.sum_o(sum_o_A),
.carry_o(carry_o_A)
);
📌 连接 ua_csa_tree
的两个输出。
🔁 行 65–71:第二个递归子树实例化
csa_tree #(
.N(N_B),
.WIDTH_I(WIDTH_I),
.WIDTH_O(WIDTH_O)
) ub_csa_tree(
.operands_i(operands_i_B),
.sum_o(sum_o_B),
.carry_o(carry_o_B)
);
📌 同样使用递归调用处理后半部分 B
的 N_B
个操作数。
🔧 行 73–74:组合两个子树的输出作为最终压缩输入
logic [3:0][WIDTH_O-1:0] operands_i_C;
assign operands_i_C = '{sum_o_A, carry_o_A, sum_o_B, carry_o_B};
📌 将两个子树共计四组输出作为新的输入 operands_i_C
,用于最后一级压缩。
🧩 行 76–78:调用最终一级 compressor_4to2
compressor_4to2 #(
.WIDTH_I(WIDTH_O),
.WIDTH_O(WIDTH_O)
📘 将 4 个中间结果压缩成最终的 sum 和 carry 输出,结束整个 CSA Tree 的压缩过程。
是否继续查看这部分的模块实例连接和 endmodule
?
我们完成对 csa_tree.sv
的分析。以下是最后几行的内容:
🔧 行 81–84:完成 compressor_4to2
实例化
) uc_compressor_4to2(
.operands_i(operands_i_C),
.sum_o(sum_o),
.carry_o(carry_o)
);
📌 接收之前拼接的四组操作数 operands_i_C
,输出最终压缩的 sum_o
和 carry_o
。
🔚 行 85–87:结束 generate
和模块
end
endgenerate
endmodule
✅ 总结:CSA Tree 功能与结构
层级 | 操作 |
---|---|
N = 1 | 直接输出 |
N = 2 | sum = op[0], carry = op[1] |
N = 3 | 使用 compressor_3to2 |
N = 4 | 使用 compressor_4to2 |
N > 4 | 分为两半递归调用,再用 compressor_4to2 合并输出 |
该结构高效压缩任意数量的同位宽输入,是 Booth/Wallace 乘法器中的关键模块。
是否将其转换为 Verilog-2001 格式?(去除 logic
、generate
简化为结构化 if
)