寻找第一个1

输入一个向量,如何从LSB或者MSB或者反过来找出第一个出现1的位置并生成独热码?这个问题碰到好多次了,今天总结一下。

这个电路有点类似于一个优先编码器(所以该电路就是固定优先级的仲裁器),例如对于一个4bit位宽的向量来说,其真值表如下

Input Output
0001 0001
xx10 0010
x100 0100
1000 1000

上述真值表的Verilog电路实现如下

module OneHot(
    input [3:0] din,
    output [3:0] onehot
);
    reg [3:0] onehot_w;
    always@(*)begin
        casex(din)
            4'b0001:onehot_w = 4'b0001;
            4'b001x:onehot_w = 4'b0010;
            4'b01xx:onehot_w = 4'b0100;
            4'b1000:onehot_w = 4'b1000;
        endcase
    end
    assign onehot = onehot_w;
endmodule

第二种方法是一个比较巧妙的方法,并且进行参数化实现也比较方便。首先有这样一种求一个数的补码的方法:从LSB到MSB,找到第一个为1的位置,该位保持不变,之后一直到MSB所有的位都取反,即可得到该数的补码。稍加思索就可以发现,既然可以用这种方法找到补码,那换句话说补码就一定符合这个规则,那么补码就可以作为mask来找到第一个1出现的地方了:因为补码是从第一个1之后所有的位都取反,那么将原数与补码按位与,就得到了一个独热码,并且该独热码1出现的位置就是原数第一个1出现的位置。

module onehot#(
    parameter WIDTH = 8
)(
    input  [WIDTH-1:0] din,
    output [WIDTH-1:0] onehot
);
    wire [WIDTH-1:0] complement_w = ~(din-1);
    assign onehot = din&complement_w;
endmodule

再添加一个参数用于调整寻找的方向,最终的设计如下

module onehot#(
    parameter WIDTH = 8,
    parameter DIRECTION = 0
)(
    input  [WIDTH-1:0] din,
    output [WIDTH-1:0] dout
);
    wire [WIDTH-1:0] din_w;
    wire [WIDTH-1:0] dout_w;
    genvar i;
    generate
        for(i=0;i<WIDTH;i=i+1)begin
            if(DIRECTION==0)begin:gen_lsb_msb
                assign din_w[i] = din[i];
                assign dout[i] = dout_w[i];
            end
            else begin:gen_msb_lsb
                assign din_w[i] = din[WIDTH-1-i];
                assign dout[i] = dout_w[WIDTH-1-i];
            end
        end
    endgenerate

    wire [WIDTH-1:0] complement_w = ~(din_w-1);
    assign dout_w = din_w&complement_w;
endmodule
posted on 2024-05-10 10:54  cmossss  阅读(281)  评论(0)    收藏  举报