先放函数,已验证和$clog2输出一致,注意需满足输入n ≥ 1。
// 返回以2为底的n的对数
function integer clog2 (input integer n); begin
n = n - 1;
for (clog2 = 0; n > 0; clog2 = clog2 + 1)
n = n >> 1;
end
endfunction
另外,经评论区提醒,在Vivado 2017以后的版本中,可以直接使用系统函数$clog2(),不需要去自编函数了。后续又测试了Quartus 18.1 同样支持$clog2(),各位可自行测试使用的工具是否支持$clog2()。
一.为什么需要以2为底n的对数的函数
在Verilog编写代码过程中,经常需要根据一个常量来定义一个变量的位宽,例如,要编写一个计数器,计数最大值为常量N,那么计数变量cnt的位宽应该是多少呢?这个问题在SV可利用系统函数$clog2来解决,如下:
module counter
#(
parameter CNT_MAX = 1023
)(
output logic cnt_finish,
input logic clk,
input logic rstn
);
logic [$clog2(CNT_MAX+1)-1 : 0] cnt;
always_ff @(posedge clk, negedge rstn) begin
if (~rstn)
cnt <= '0;
else if (cnt < CNT_MAX)
cnt <= cnt + 1'b1;
else
cnt <= '0;
end
assign cnt_finish = cnt == CNT_MAX;
endmodule
但是在Verilog中没有这个系统函数,所以需要自行编写。
二.Verilog编写求以2为底n的对数的函数
将上面计数器的SV代码改为Verilog,用自编的clog2替代SV的系统函数$clog2
module counter
#(
parameter CNT_MAX = 1023
)(
output wire cnt_finish,
input wire clk,
input wire rstn
);
reg [clog2(CNT_MAX+1)-1 : 0] cnt;
always @(posedge clk, negedge rstn) begin
if (~rstn)
cnt <= 'd0;
else if (cnt < CNT_MAX)
cnt <= cnt + 1'b1;
else
cnt <= 'd0;
end
assign cnt_finish = cnt == CNT_MAX;
// 以2为底的对数函数
function integer clog2 (input integer n); begin
n = n - 1;
for (clog2 = 0; n > 0; clog2 = clog2 + 1)
n = n >> 1;
end
endfunction
endmodule
三.仿真验证
仿真验证自编函数clog2的输出和SV系统函数$clog2的输出是否一致,仿真工具:modelsim SE-64 2020.4,testbench(try_tb.sv)如下:
module try_tb();
timeunit 1ns;
timeprecision 10ps;
initial begin
for (integer i=1; i<1025; i++) begin
$display("$clog(%d) = %d, clog2(%d) = %d", i, $clog2(i), i, clog2(i));
end
end
// 以2为底的对数函数
function integer clog2 (input integer n); begin
n = n - 1;
for (clog2 = 0; n > 0; clog2 = clog2 + 1)
n = n >> 1;
end
endfunction
endmodule
部分仿真结果:
可见,clog2(n) 与 $clog2(n) 输出一致。