Verilog代码的一些思考
一、怎么表示可变位宽变量的最大值?
在对RTL模块的设计时,我们有时候需要取某个变量能表示的最大值,对于我们确定位宽的变量,我们可以直接表示其最大值,例如:
// 变量位宽为4
module tb
(
);
reg [3:0] temp;
always @(*) temp <= 4'b1111;
endmodule
实际上,要求模块的可复用性,在很多时候内部变量的位宽在模块例化的时候是可以修改的,例如:
// 变量位宽为4
module tb
#(
paramter Dat_W = 4
)
();
reg [Dat_W-1:0] temp;
always @(*) temp <= 4'b1111;
endmodule
这里,如果我们在例化该模块时修改了Dat_W的值,修改为更大的值,比如6,那么这里4’b1111所表示的并不是位宽为6所能表示的最大值,实际上的最大值应该为6’b111111。那么,我们可以通过位拼接的方式取其最大值,如下:
// 变量位宽为4
module tb
#(
paramter Dat_W = 4
)
();
reg [Dat_W-1:0] temp;
always @(*) temp <= {(Dat_W){1'b1}};
endmodule
可以参考:HDL Bits例题
二、怎么快速计算某个变量二进制表达中“1”的个数?
怎么统计一个二进制数中1的个数?比如,二进制数8’b11001010中1的个数为4,怎么得到4?参考博客《统计1的个数(Verilog)》,其代码如下:
module test(
input [7:0]data_in,
output [3:0]out
);
// 写法一:
reg [3:0]width;
reg [3:0]cnt;
always@(data_in)begin
cnt = 'd0;
for(width = 0; width < 8; width = width + 1)begin
if(data_in[width])
cnt = cnt + 1'b1;
else
cnt = cnt;
end
end
assign out = cnt;
// 写法二:
// assign out = data_in[0] + data_in[1] + data_in[2] + data_in[3] + data_in[4] + data_in[5] + data_in[6] + data_in[7];
endmodule
为了方便对不同位宽的数据进行统计,而不需重复修改代码内部,也可以这么写:
module test
#(
parameter Din_W = 8,
parameter Dout_W = $clog2(Din_W)
)
(
input [Din_W-1:0]data_in,
output [Dout_W-1:0]out
);
// 写法三:
wire [Dout_W-1:0] out_wire [Din_W-1:0];
genvar i;
generate
for(i = 0;i<Din_W;i = i + 1) begin:number_cal
if(i == 0)
assign out_wire[i] = data_in[i];
else
assign out_wire[i] = data_in[i] + out_wire[i-1];
end
endgenerate
assign out = out_wire[Din_W-1];
endmodule
写法一:RTL视图
资源使用报告
写法二:RTL视图
资源使用报告
写法三:RTL视图
资源使用报告
三、位索引[0:3]与[3:0]的区别?
在Verilog中对变量进行声明时,有时候会见到以下两种声明情况:
wire [7:0] a;
wire [0:7] b;
wire [3:0] c;
wire [0:3] d;
那么这两种的区别在哪里?
第一,对于变量的部分索引必须与变量声明的顺序一致(升序或者降序),比如:
assign c = a[7:4]; //正确索引
assign c = a[4:7]; //错误索引
assign d = b[0:3]; //正确索引
assign d = b[3:0]; //错误索引
第二,对于变量赋同一值,两种低位和高位是不一致的,比如:
assign a = 8'b10101100; //左边第一位为最高位,即a[7]=1;右边第一位为最低位,即a[0]=0;
assign b = 8'b10101100; //左边第一位为最低位,即a[0]=1;右边第一位为最高位,即a[7]=0;
wire e,f;
assign e = a[0]; //e=0
assign f = b[0]; //f=1
在有些设计中,我们需要使用正序索引,而有些情况下使用反序索引会更方便,可以根据需求进行选择。
参考:HDL Bits例题
四、if-else在组合逻辑中的等价替换——三目运算符的嵌套
在时序逻辑中,对于一个变量在不同条件下进行赋值,我们可以使用if-else进行条件判断,然后进行赋值,那么在组合逻辑中,我们可以使用三目运算符进行替换,比如:
always @(posedge clk)
if(条件1)
a = 数值1;
else
a = 数值2;
那么,在组合逻辑中,我们可以用三目运算符进行替换,如下:
assign a = (条件1) ? 数值1 : 数值2;
而对于if-else的多级嵌套,同样可以使用三目运算符替换,例如:
always @(posedge clk)
if(条件1)
a = 数值1;
else if(条件2)
a = 数值2;
else if(条件3)
a = 数值3;
else
a = 数值4;
同样,在组合逻辑中可以使用三目运算符嵌套进行替换,如下:
assign a = 条件1 ? 数值1 : 条件2 ? 数值2 : 条件3 ? 数值3 : 数值4;
再比如:
always @(posedge clk)
if(条件1)
a = 数值1;
else if(条件2)
if(条件3)
a = 数值2;
else
a = 数值3;
else
a = 数值4;
在组合逻辑中可以替换为:
assign a = 条件1 ? 数值1 : 条件2 ? (条件3 ? 数值2 : 数值3) : 数值4;
虽然if-else在组合逻辑的等价替换——三目运算符可以实现同样的功能,但是这样一来,对于多重的三目运算符嵌套,会造成路径延时过大的问题。
五、
六、
未完待续… … …