在输入数据中找到1的位置:找到第一个1的位置和最后一个1的位置。
给出了两种设计方法:第一种使用二分法,但是有一定局限性;第二种则是通用的参数化设计方法。
目录
一、二分法实现
二、参数化实现
一、二分法实现
输入一个数据,输出第一个1所在的位置和最后一个1所在的位置。
代码实现:
module pos(
input clk,
input rst_n,
input [7:0] din,
output [2:0] first_pos,
output [2:0] last_pos
);
wire [3:0] first_data_4;
wire [1:0] first_data_2;
wire [3:0] last_data_4;
wire [1:0] last_data_2;
assign first_pos[2] = ~|din[3:0]? 1:0;
assign first_data_4 = first_pos[2]? din[7:4] : din[3:0];
assign first_pos[1] = ~|first_data_4[1:0];
assign first_data_2 = first_pos[1]? first_data_4[3:2] : first_data_4[1:0];
assign first_pos[0] = ~first_data_2[0];
assign last_pos[2] = |din[7:4];
assign last_data_4 = last_pos[2] ? din[7:4] : din[3:0];
assign last_pos[1] = |last_data_4[3:2];
assign last_data_2 = last_pos[1] ? last_data_4[3:2] : last_data_4[1:0];
assign last_pos[0] = last_data_2[1];
endmodule
第一个1的位置和最后一个1的位置都是从低位算起的位置。在这里定义输入数据是8位,一开始我想直接做成参数化,但在实现过程中遇到一些小问题,所以目前先把数据的位数固定。
主体思路是不断做二分,比如找到第一个1的逻辑
(1)首先判断输入数据低4位,按位进行或运算后取反,如果低4位中出现了1则first_pos[2]等于0,反之如果低4位中都没有出现1则first_pos[2]等于1
(2)借助中间变量first_data_4,如果first_pos[2]等于1则取输入数据的高4位(说明低4位中没有1),如果first_pos[2]等于0则取输入数据的低4位(说明低4位中有1)
(3)first_data_4再进行二分,将低两位按位或运算后取反赋值给first_pos[1],根据first_pos[1]的值再对first_data_2进行赋值
举一个典型例子,比如最高位在第7位:1000_0000
(1)首先低四位都是0,first_pos[2]值为1,first_data_4取值高四位
(2)first_data_4的低两位是00,first_pos[1]值也为1,则first_data_2的值取first_data_4的高两位
(3)first_data_2的第0位是0,则first_pos[0]值位1,得出结果是111即第7位。
找到最后一个1的位置的思路类似,取值和赋值的逻辑则需要相应取反。
测试了几个数据,波形如下:
1、0110_0000,第一个1的位置是5,最后一个1的位置是6(数据都是用二进制表示)
2、1000_0001,第一个1的位置是0,最后一个1的位置是7
3、0001_0000, 第一个1的位置和最后一个1的位置都是4
这个方法可以实现功能,但是有一定的局限性,尤其是奇数位宽的情况,做二分显然就不适用。整体逻辑还有优化的空间,尤其是参数化的实现。
二、参数化实现
先考虑找到第一个出现的1的情况。如果给输入信号减去1会得到什么?
假设din的第i位是1,第0到第i-1位都是0,那么减去1之后低位不够减,就需要向高位借位,而直到第一次出现1的位,借位才会完成。即从第i位借位,第0到i-1位都变成了1,而第i位变为了0,更高位不变。
然后再给减1之后的结果取反,然后把结果再和din本身按位与,可以得出只有第i位在取反之后又变成了1,而其余位都是和din本身相反的,按位与之后是0,这样就提取出来了第一个为1的一位。
其实减1再取反,也就是在计算2的补码。2的补码的一个特性是,一个数和它的补码相与,得到的结果是一个独热码,独热码为1的那一位是这个数最低的1。所以这个设计方法可以用一句话来概括:
输入信号和它的2的补码按位与,可以找到第一个1出现的位置。
而对于最后一个1的位置,将输入信号高低位转换一下,再同样用之前的思路即可。
代码实现:
module pos #(parameter N=9)(
input clk,rst_n,
input [N-1:0] din,
output [N-1:0] first_pos,
output [N-1:0] first_pos_out,
output [N-1:0] last_pos,
output [N-1:0] last_pos_out
);
assign first_pos = din & (~(din-1));
assign first_pos_out = $clog2(first_pos);
genvar i,j;
reg [N-1:0] tmp;
reg [N-1:0] tmp_pos;
generate
for(i=0;i<N;i=i+1)begin
assign tmp[N-1-i] = din[i];
end
endgenerate
assign tmp_pos = tmp & (~(tmp-1));
generate
for(j=0;j<N;j=j+1)begin
assign last_pos[N-1-j] = tmp_pos[j];
end
endgenerate
assign last_pos_out = $clog2(last_pos);
endmodule
为了更直观的观察结果,使用$clog2函数作了一个对数运算,直接输出位数(从低位开始算起)。
测试了几个数据,波形如下:
1、0_0110_1000
2、010000110
3、全1的情况 文章来源:https://www.toymoban.com/news/detail-499907.html
文章来源地址https://www.toymoban.com/news/detail-499907.html
到了这里,关于Verilog设计_找到1的位置的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!