[IC] lzc模块(计算头部、尾部0个数)学习
in 默认分类 with 0 comment
[IC] lzc模块(计算头部、尾部0个数)学习
in 默认分类 with 0 comment

背景

在看向量处理器ara源码的时候在sequencer、vmfpu、masku、sldu、addrgen等单元都看到了lzc单元,起初不知道这个缩写的含义,看了模块发现是一个计算头部或尾部1个数的模块trailing zero counter / leading zero counter(也就是第一个1的索引)。比如在masku中,对于mask操作数计算得到尾部0的个数,然后累加来得到vfirst指令所要获得的最低有效元素的index。

我自己用一个简单的for循环写了个LUT用DC综合发现时序差了很多。

32位
面积5287 vs. 4545
时序4.95 vs. 2.40

lzc模块分析

ara的lzc可见 https://github.com/pulp-platform/common_cells/blob/master/src/lzc.sv

我自己看了下这个模块,发现其是用了个二叉树,将待计数的输入作为虚拟"叶子"节点,然后父节点的2个子节点向上报是否有1,并且报告这个1的索引。

综合电路如


我们可以看到对于8位的输入,输出的表达式为{(0+1+2+3)', (0+1+2+3)' ? (4+5)' : (0+1)', (0+1+2+3)' ? (4+5'6)' : (0+1'2)'} ,可以看到就是二分mux,根据一半或的结果来选择哪一半的结果

模仿

然后我根据自己的理解,添加了一个target输入来表示是识别0还是1,参数HIGHEST表示从高到低识别。不过这里很多边界情况没有做处理。

当前节点编号为i,则其子节点的编号为$i\times 2+1$和$i\times 2+2$

module lzc_target #(
    parameter int WIDTH   = 8,
    parameter int HIGHEST = 0
) (
    input [WIDTH-1:0]           data_i,
    input                       target,
    output                      exit_o,
    output [$clog2(WIDTH)-1:0]  pos_o
);
    localparam LEVELS = $clog2(WIDTH);
    localparam N      = 2**LEVELS;
    localparam N_2    = 2**(LEVELS-1);

    logic [N-1:0][LEVELS-1:0]   indices;
    logic [N-1:0]               sel_node;
    logic [N-1:0][LEVELS-1:0]   index_node;

    `define INDEX (2**l-1+i)

    for (genvar i = 0; i < WIDTH; i=i+1) begin
        assign indices[i] = (LEVELS)'(unsigned'(i));
    end

    for (genvar l = 0; l < LEVELS; l=l+1) begin
        if (l == LEVELS-1) begin: gen_last_level
            for (genvar i = 0; i < N_2; i=i+1) begin
                assign sel_node[N_2-1+i] = target ? (data_i[i*2] | data_i[i*2+1]) : (data_i[i*2] & data_i[i*2+1]);
                if (HIGHEST)
                    assign index_node[N_2-1+i] = data_i[i*2+1] == target ? indices[i*2+1] : indices[i*2];
                else
                    assign index_node[N_2-1+i] = data_i[i*2] == target ? indices[i*2] : indices[i*2+1];
            end
        end else begin: gen_each_level
            for (genvar i = 0; i < 2**l; i=i+1) begin: gen_each_node
                assign sel_node[`INDEX] = target ? (sel_node[`INDEX*2+1] | sel_node[`INDEX*2+2]) : (sel_node[`INDEX*2+1] & sel_node[`INDEX*2+2]);
                if (HIGHEST)
                    assign index_node[`INDEX] = sel_node[`INDEX*2+2] == target ? index_node[`INDEX*2+2] : index_node[`INDEX*2+1];
                else
                    assign index_node[`INDEX] = sel_node[`INDEX*2+1] == target ? index_node[`INDEX*2+1] : index_node[`INDEX*2+2];
            end
        end
    end
    assign pos_o = index_node[0];
    assign exit_o = sel_node[0] == target;
endmodule
Responses