Verilog 统计第一个1后0的个数/1的个数/第一个1的位置
一、前言
当数据的位宽不是很长时,此类问题可以使用移位寄存器来解决。我们将输入数据不断的右移,这样每次只需要对最后一bit进行判断。由于需要统计个数,我们还需要定义一些计数器,cnt用来计数已经处理了多个bit,而cnt0和cnt1用来记录要统计的0和1的个数。
当然还需要一些其他逻辑辅助完成这些功能,可以参考下面的代码,为了方便理解,里面也写了一些注释。
二、从低到高统计数据中第一个1后面的0的个数
/*
从低到高 统计第一个1后面的0的个数
*/
module cnt_num(
input clk,
input rst_n,
input din_en,
input [7:0]din,
output reg[3:0]count_out
);
reg [3:0]cnt;//计数检测了多少bit
reg [3:0]cnt0;//计数0的个数
reg flag;//检测到了第一个1
reg [7:0]din_r;//din的移位寄存器
reg din_en_r;
wire pos_din_en;
localparam BitWidth = 8;
always@(posedge clk or negedge rst_n)begin
if(!rst_n)
din_en_r <= 1'b0;
else
din_en_r <= din_en;
end
assign pos_din_en = din_en && (!din_en_r);
always@(posedge clk or negedge rst_n)begin
if(!rst_n)
cnt <= 4'd0;
else if(din_en && (cnt < BitWidth))
cnt <= cnt + 1'b1;
else
cnt <= 4'd0;
end
//当flag有效时,就开始判断每一bit是不是0
//是的话,cnt0计数+1;否则就保持不变,当所有bit检测完后归零
always@(posedge clk or negedge rst_n)begin
if(!rst_n || pos_din_en)
cnt0 <= 4'd0;
else if(flag && (din_r[0] == 0))
cnt0 <= cnt0 + 1'b1;
else if (cnt < BitWidth) begin
cnt0 <= cnt0;
end
else
cnt0 <= 4'd0;
end
//判断是否已经检测到了第一个1
always@(posedge clk or negedge rst_n)begin
if(!rst_n)
flag <= 1'b0;
else if(din_r[0])
flag <= 1'b1;
else if (cnt == BitWidth) begin
flag <= 1'b0;
end
else
flag <= flag;
end
//din的右移移位寄存器,这样只需要每次检测最低位即可
always@(posedge clk or negedge rst_n)begin
if(!rst_n)
din_r <= 8'd0;
else if(pos_din_en)
din_r <= din;
else if(din_en)
din_r <= {0,din_r[7:1]};
else
din_r <= din_r;
end
//在cnt=0时将计数结果赋给count_out
//没有在cnt=8时赋值,是因为cnt0的逻辑比cnt1延迟了一个周期,因为前者受flag的影响
always@(posedge clk or negedge rst_n)begin
if(!rst_n)
count_out <= 4'd0;
else if(cnt == 4'd0)
count_out <= cnt0;
else
count_out <= count_out;
end
endmodule
三、统计数据中1的个数
/*
统计1的个数
*/
module cnt_num2(
input clk,
input rst_n,
input din_en,
input [7:0]din,
output reg[3:0]count_out
);
reg [3:0]cnt;//计数检测了多少bit
reg [3:0]cnt1;//计数1的个数
reg [7:0]din_r;//din的移位寄存器
reg din_en_r;
wire pos_din_en;
localparam BitWidth = 8;
always@(posedge clk or negedge rst_n)begin
if(!rst_n)
din_en_r <= 1'b0;
else
din_en_r <= din_en;
end
assign pos_din_en = din_en && (!din_en_r);
always@(posedge clk or negedge rst_n)begin
if(!rst_n)
cnt <= 4'd0;
else if(din_en && (cnt < BitWidth))
cnt <= cnt + 1'b1;
else
cnt <= 4'd0;
end
//检测到1就加1,否则保持不变,检测完所有bit归零
always@(posedge clk or negedge rst_n)begin
if(!rst_n || pos_din_en)
cnt1 <= 4'd0;
else if(din_r[0] == 1)
cnt1 <= cnt1 + 1'b1;
else if (cnt < BitWidth) begin
cnt1 <= cnt1;
end
else
cnt1 <= 4'd0;
end
//din的右移移位寄存器,这样只需要每次检测最低位即可
always@(posedge clk or negedge rst_n)begin
if(!rst_n)
din_r <= 8'd0;
else if(pos_din_en)
din_r <= din;
else if(din_en)
din_r <= {0,din_r[7:1]};
else
din_r <= din_r;
end
always@(posedge clk or negedge rst_n)begin
if(!rst_n)
count_out <= 4'd0;
else if(cnt == BitWidth)
count_out <= cnt1;
else
count_out <= count_out;
end
endmodule
四、获取第一个1的位置
/*
从低到高 记录第一个1的位置
*/
module cnt_num3(
input clk,
input rst_n,
input din_en,
input [7:0]din,
output reg[2:0]index_out
);
reg [3:0]cnt;//计数检测了多少bit
reg flag;//检测到了第一个1
reg [7:0]din_r;//din的移位寄存器
reg [2:0]index;//记录第一个1的位置
reg din_en_r;
wire pos_din_en;
localparam BitWidth = 8;
always@(posedge clk or negedge rst_n)begin
if(!rst_n)
din_en_r <= 1'b0;
else
din_en_r <= din_en;
end
assign pos_din_en = din_en && (!din_en_r);
always@(posedge clk or negedge rst_n)begin
if(!rst_n)
cnt <= 4'd0;
else if(din_en && (cnt < BitWidth))
cnt <= cnt + 1'b1;
else
cnt <= 4'd0;
end
//判断是否已经检测到了第一个1
always@(posedge clk or negedge rst_n)begin
if(!rst_n)begin
index <= 3'd0;
flag <= 1'b0;
end
else if((!flag) && din_r[0] && (cnt < BitWidth))begin
flag <= 1'b1;
index <= cnt;
end
else if (cnt == BitWidth) begin
flag <= 1'b0;
index <= 3'd0;
end
else begin
flag <= flag;
index <= index;
end
end
//din的右移移位寄存器,这样只需要每次检测最低位即可
always@(posedge clk or negedge rst_n)begin
if(!rst_n)
din_r <= 8'd0;
else if(pos_din_en)
din_r <= din;
else if(din_en)
din_r <= {0,din_r[7:1]};
else
din_r <= din_r;
end
//在cnt=0时将计数结果赋给count_out
//没有在cnt=8时赋值,是因为cnt0的逻辑比cnt1延迟了一个周期,因为前者受flag的影响
always@(posedge clk or negedge rst_n)begin
if(!rst_n)
index_out <= 3'd0;
else if(cnt == BitWidth)
index_out <= index;
else
index_out <= index_out;
end
endmodule
需要注意的是代码中的一些逻辑,需要根据输入数据din和使能信号din_en的实际时序做出一些修改,但核心思想是差不多的!!!