动机
想弄一个数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的多一些。