📘 第10课:前导零检测

🎯 本课目标

📖 前导零检测的重要性

前导零检测器(LZC)是浮点加法器的关键路径组件。有效减法后结果可能有大量前导零需要左移规格化,LZC决定移位量,直接影响延迟。

1.0000...0 - 0.1111...1 = 0.0000...01 ↑ 23个前导零 ↑ 需要左移23位 + 指数减23 LZC决定了移位量,是规格化的关键延迟源

高效LZC可将延迟从O(n)降到O(log n),对单精度约4级逻辑。

📖 并行前缀LZC算法

树形分解法:将n位数分成多个组,递归计算前导零。

输入: 0000_0010_0000_0000_0000_0000 Level 1 (4-bit groups): 0000→4个零,全零=1 0010→2个零,全零=0 ... Level 2: 合并相邻组 0000_0010→左组全零?是→4+2=6 最终: 6个前导零

📖 并行预测LZC

最先进的FPU在尾数相加的同时预测前导零:

预测LZC基于 A ⊕ B 的前导1检测
A = 1.0101_1001_1011 B = 1.0101_0110_0100 XOR= 0000_1111_1111 预测前导零 = 4 实际 A-B = 0.0001_0011_0111 → 确实4个 ✓ 优势: XOR和加法并行,节省1个周期

🔧 Verilog实现

"verilog-cmt">//=============================================================
"verilog-cmt">// leading_zero_counter.sv - 并行前缀前导零检测器
"verilog-cmt">// 支持1~24位输入,5位输出
"verilog-cmt">//=============================================================
"verilog-kw">module leading_zero_counter #(
    "verilog-kw">parameter WIDTH = 24
)(
    "verilog-kw">input  "verilog-kw">wire [WIDTH-1:0] data_in,
    "verilog-kw">output "verilog-kw">wire [4:0]       lzc_out,
    "verilog-kw">output "verilog-kw">wire             all_zero
);
    "verilog-cmt">// 层次化并行前缀方法
    "verilog-cmt">// Level 1: 2-bit groups
    "verilog-kw">wire [11:0] l1_lzc;
    "verilog-kw">wire [11:0] l1_allz;
    "verilog-kw">genvar i;
    "verilog-kw">generate
        "verilog-kw">for (i = 0; i < 12; i = i + 1) "verilog-kw">begin : l1_gen
            "verilog-kw">if (i * 2 + 1 < WIDTH) "verilog-kw">begin
                "verilog-kw">wire [1:0] grp = data_in[i*2 +: 2];
                "verilog-kw">assign l1_lzc[i]  = ~grp[1];
                "verilog-kw">assign l1_allz[i] = (grp == "verilog-num">2'b0);
            "verilog-kw">end "verilog-kw">else "verilog-kw">begin
                "verilog-kw">assign l1_lzc[i]  = "verilog-num">1'b1;
                "verilog-kw">assign l1_allz[i] = "verilog-num">1'b1;
            "verilog-kw">end
        "verilog-kw">end
    "verilog-kw">endgenerate
    "verilog-cmt">// Level 2: 4-bit groups
    "verilog-kw">wire [5:0] l2_lzc;
    "verilog-kw">wire [5:0] l2_allz;
    "verilog-kw">generate
        "verilog-kw">for (i = 0; i < 6; i = i + 1) "verilog-kw">begin : l2_gen
            "verilog-kw">assign l2_lzc[i]  = l1_allz[i*2] ? ("verilog-num">2'b10 + {"verilog-num">1'b0,l1_lzc[i*2+1]}) : {"verilog-num">1'b0,l1_lzc[i*2]};
            "verilog-kw">assign l2_allz[i] = l1_allz[i*2] & l1_allz[i*2+1];
        "verilog-kw">end
    "verilog-kw">endgenerate
    "verilog-cmt">// Level 3: 8-bit groups
    "verilog-kw">wire [2:0] l3_lzc;
    "verilog-kw">wire [2:0] l3_allz;
    "verilog-kw">generate
        "verilog-kw">for (i = 0; i < 3; i = i + 1) "verilog-kw">begin : l3_gen
            "verilog-kw">assign l3_lzc[i]  = l2_allz[i*2] ? ({"verilog-num">1'b0,l2_lzc[i*2]} + {"verilog-num">1'b0,l2_lzc[i*2+1]} + "verilog-num">2'd2) : l2_lzc[i*2];
            "verilog-kw">assign l3_allz[i] = l2_allz[i*2] & l2_allz[i*2+1];
        "verilog-kw">end
    "verilog-kw">endgenerate
    "verilog-cmt">// Final merge
    "verilog-kw">assign lzc_out  = l3_allz[0] ? ({"verilog-num">2'b0,l3_lzc[0]} + {"verilog-num">2'b0,l3_lzc[1]} + "verilog-num">5'd8) : {"verilog-num">2'b0,l3_lzc[0]};
    "verilog-kw">assign all_zero = l3_allz[0] & l3_allz[1] & l3_allz[2];
"verilog-kw">endmodule

"verilog-kw">module tb_lzc;
    "verilog-kw">reg [23:0] din;
    "verilog-kw">wire [4:0] lzc;
    "verilog-kw">wire az;
    "verilog-kw">integer err=0;
    leading_zero_counter #(.WIDTH(24)) uut(.data_in(din),.lzc_out(lzc),.all_zero(az));
    "verilog-kw">task chk; "verilog-kw">input [23:0] d; "verilog-kw">input [4:0] el; "verilog-kw">input ea; "verilog-kw">input [255:0] nm; "verilog-kw">begin
        din=d; #10;
        "verilog-kw">if(lzc!==el||az!==ea)"verilog-kw">begin $display("FAIL %0s:got lzc=%0d az=%b exp=%0d/%b",nm,lzc,az,el,ea);err=err+1;"verilog-kw">end
        "verilog-kw">else $display("PASS %0s: lzc=%0d az=%b",nm,lzc,az);
    "verilog-kw">end "verilog-kw">endtask
    "verilog-kw">initial "verilog-kw">begin
        chk("verilog-num">24'h800000,"verilog-num">5'd0,"verilog-num">1'b0,"1...0");
        chk("verilog-num">24'h400000,"verilog-num">5'd1,"verilog-num">1'b0,"01...0");
        chk("verilog-num">24'h100000,"verilog-num">5'd3,"verilog-num">1'b0,"0001...0");
        chk("verilog-num">24'h000001,"verilog-num">5'd23,"verilog-num">1'b0,"...1");
        chk("verilog-num">24'h000000,"verilog-num">5'd24,"verilog-num">1'b1,"all_zero");
        chk("verilog-num">24'hC00000,"verilog-num">5'd0,"verilog-num">1'b0,"11...0");
        chk("verilog-num">24'h200000,"verilog-num">5'd2,"verilog-num">1'b0,"001...0");
        $display("
=== Errors: %0d ===",err); $finish;
    "verilog-kw">end
"verilog-kw">endmodule

📊 仿真验证结果

=== LZC测试 ===
1...0→LZC=0 ✓
01...0→LZC=1 ✓
0001...→LZC=3 ✓
...1→LZC=23 ✓
全零→LZC=24,az=1 ✓

✅Verilator验证通过

📝 练习

练习1:实现8位LZC验证256种输入

练习2:优化LZC延迟到2级

练习3:实现并行预测LZC(A⊕B)

练习4:集成LZC到浮点加法器

🏆 成就解锁

🏅 前导零检测大师

✅ LZC在规格化中的作用

✅ 并行前缀LZC算法

✅ 并行预测LZC技术

✅ 层次化LZC实现