verilog数1的个数参数化位宽

动机

想弄一个数1个数的电路,并且能够参数化,尝试了几种写法,记录一下。

系统函数$countones

先试试verilog自带的。

`define PORT_NUM 16
module test1_tb();
test1 U_test1(.access_port_r(16'b0100_0001_0011_1001));
endmodule
module test1(
(* DONT_TOUCH= "TRUE" *)    input [`PORT_NUM-1:0] access_port_r
);
(* DONT_TOUCH= "TRUE" *) wire [$clog2(`PORT_NUM):0] one_in_access_port_o;

assign one_in_access_port_o = $countones(access_port_r);

endmodule

在这里插入图片描述
结果不太行,仿真都不行,所以就不试综合了。

代码段1

源自链接:https://www.javaroad.cn/questions/156207#
按照其中的思想,重写代码。

`define PORT_NUM 16
module test1_tb();
test1 U_test1(.access_port_r(16'b0100_0001_0011_1001));
endmodule
module test1(
(* DONT_TOUCH= "TRUE" *)    input [`PORT_NUM-1:0] access_port_r
);
(* DONT_TOUCH= "TRUE" *) wire [$clog2(`PORT_NUM):0] one_in_access_port_o;

integer i;
reg [$clog2(`PORT_NUM):0] one_in_access_port;
always @(*) begin
    for (i=0;i<(`PORT_NUM);i=i+1) begin
        one_in_access_port = one_in_access_port + access_port_r[i];
    end
end
assign one_in_access_port_o = one_in_access_port;

endmodule

在这里插入图片描述
仿真结果不行,而且这段代码不太清楚会综合成什么。

代码段2

代码段1中的主要问题是,one_in_access_port会回环给自己,不太合理。改成数组进行尝试。

`define PORT_NUM 16
module test1_tb();
test1 U_test1(.access_port_r(16'b0100_0001_0011_1001));
endmodule
module test1(
(* DONT_TOUCH= "TRUE" *)    input [`PORT_NUM-1:0] access_port_r
);
(* DONT_TOUCH= "TRUE" *) wire [$clog2(`PORT_NUM):0] one_in_access_port_o;

integer i;
(* DONT_TOUCH= "TRUE" *) reg [$clog2(`PORT_NUM):0] one_in_access_port [`PORT_NUM-1:0];
always @(*) begin
    one_in_access_port[0] = access_port_r[0];
    for (i=1;i<(`PORT_NUM);i=i+1)
        one_in_access_port[i] = one_in_access_port[i-1] + access_port_r[i];
end
assign one_in_access_port_o = one_in_access_port[`PORT_NUM-1];

endmodule

在这里插入图片描述
仿真结果没有问题。但这个电路想了一下,应该会综合成链式的加法器,级数比较多。
用vivado综合一下(综合之前需要把test1_tb()注释掉,不然会干扰综合结果)。
在这里插入图片描述
放大看一下。
在这里插入图片描述
每bit的输入自动加了一个IBUF,不过确实是按bit逐位相加。
在这里插入图片描述
用到了84个LUT。

代码段3

代码段2已经可以工作了,但并不是最好的,最好可以用加法树。因此写了如下代码。

`define PORT_NUM 16
module test1_tb();
test1 U_test1(.access_port_r(16'b0100_0001_0011_1001));
endmodule
module test1(
(* DONT_TOUCH= "TRUE" *)    input [`PORT_NUM-1:0] access_port_r
);
(* DONT_TOUCH= "TRUE" *) wire [$clog2(`PORT_NUM):0] one_in_access_port_o;

genvar j;
(* DONT_TOUCH= "TRUE" *) wire [`PORT_NUM-1:0] PARA [$clog2(`PORT_NUM)-1:0];
generate for (j=1;j<($clog2(`PORT_NUM)+1);j=j+1)
    assign PARA[j-1] = {(`PORT_NUM/(2**j)){{(2**(j-1)){1'b0}},{(2**(j-1)){1'b1}}}};
endgenerate
integer i;
(* DONT_TOUCH= "TRUE" *) reg [`PORT_NUM-1:0] one_in_access_port [$clog2(`PORT_NUM):0];
always @(*) begin
    one_in_access_port[0] = access_port_r;
    for (i=1;i<($clog2(`PORT_NUM)+1);i=i+1) begin
        one_in_access_port[i] = (one_in_access_port[i-1]&PARA[i-1])
                               +((one_in_access_port[i-1]>>(2**(i-1)))&PARA[i-1]);
    end
end
assign one_in_access_port_o = one_in_access_port[$clog2(`PORT_NUM)];

endmodule

在这里插入图片描述
仿真结果无误。不过这段代码在写的时候,一开始PARA参数是写在下面的always块里面,报了Repetition multiplier must be constant的语法错误,结果放在assign里面就可以了。
综合一下(同样需要把test1_tb()注释掉)。
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
大致来看,级数是减少了。

这段代码的思想,实际上是用掩码来做的求和。
在这个链接里面有介绍:https://www.cnblogs.com/graphics/archive/2010/06/21/1752421.html
在这里插入图片描述
截了一张图。

再仔细看下综合结果。
在这里插入图片描述
可以看到,综合出来的PARA参数也使用LUT进行了保存,本来以为会直接接地或者接高电平。
在这里插入图片描述
用到了133个LUT,比代码段2的多一些。

  • 1
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值