我正在使用 Xilinx Vivado 2023.2(在 Windows 11 上)。以下代码应该计算输入信号的峰峰值和平均值。 由于未知原因,Vivado 模拟器有时会跳过(Peak_Mean.sv 的)最后两个
always
块(peak_reg 和mean_reg)。
在这个小设置中,
result.txt
与 ref.txt
相同。然而,该模块只是一个更大项目的一小部分,这种行为是不能容忍的。
Peak_Mean.sv
`timescale 1ns / 1ps
module Peak_Mean #
(
parameter SYSTEM_FREQUENCY = 100000000, // [Hz]
parameter frequency = 1000 // [Hz] // max. SYSTEM_FREQUENCY/duty_window // Note: SYSTEM_FREQUENCY/frequency should be an integer to ensure expected behavior
)
(
input clk,
input nReset,
input enable,
input signed [ 17 : 0 ] in_signal,
output [ 17 : 0 ] peak_peak, // no sign bit (always positive)
output signed [ 17 : 0 ] mean
);
localparam sample = SYSTEM_FREQUENCY/frequency;
reg [ $clog2(sample)-1 : 0 ] counter_freq;
always@( posedge clk ) begin
if( !nReset || (counter_freq == sample-1) ) begin
counter_freq <= 0;
end else begin
++counter_freq;
end
end
reg signed [ 17 : 0 ] max_reg;
always@( posedge clk ) begin
if( !nReset || (counter_freq == sample-2) ) begin
max_reg <= 18'b100000000000000000;
end else begin
if( enable ) begin
if( !in_signal[17] ) begin
if( (in_signal > max_reg) || max_reg[17] ) begin
max_reg <= in_signal;
end
end else begin
if( ((~in_signal+1) < (~max_reg+1)) && max_reg[17] ) begin
max_reg <= in_signal;
end
end
end
end
end
reg signed [ 17 : 0 ] min_reg;
always@( posedge clk ) begin
if( !nReset || (counter_freq == sample-2) ) begin
min_reg <= 18'b011111111111111111;
end else begin
if( enable ) begin
if( !in_signal[17] ) begin
if( (in_signal < min_reg) && !min_reg[17] ) begin
min_reg <= in_signal;
end
end else begin
if( ((~in_signal+1) > (~min_reg+1)) || !min_reg[17] ) begin
min_reg <= in_signal;
end
end
end
end
end
reg [ 17 : 0 ] peak_reg;
always@( posedge clk ) begin
if( !nReset ) begin
peak_reg <= 0;
end else begin
if( counter_freq == sample-3 ) begin
peak_reg <= max_reg - min_reg;
end
end
end
reg signed [ 18 : 0 ] mean_reg;
always@( posedge clk ) begin
if( !nReset ) begin
mean_reg <= 0;
end else begin
if( counter_freq == sample-3 ) begin
mean_reg <= (max_reg + min_reg) >>> 1;
end
end
end
assign peak_peak = peak_reg;
assign mean = mean_reg;
endmodule
tb_Peak_Mean.sv(注:signal.hex 和 result.txt 路径)
`timescale 1ns / 1ps
module tb_Peak_Mean();
localparam TEST_LEN = 32;
localparam SYSTEM_FREQUENCY = 100000000;
localparam frequency = 1000;
localparam sample = SYSTEM_FREQUENCY/frequency;
// system
reg clk;
reg nReset;
// stimuli
reg [ $clog2(sample)-1 : 0 ] sample_counter;
reg [ $clog2(TEST_LEN) : 0 ] counter_tb_state;
reg signed [ 17 : 0 ] testvec_in_signal [ 0 : TEST_LEN-1 ];
// dut io
reg enable;
wire signed [ 17 : 0 ] in_signal = testvec_in_signal[ counter_tb_state ];
wire [ 17 : 0 ] peak_peak;
wire signed [ 17 : 0 ] mean;
// dump file
integer f;
// DUT
Peak_Mean #
(
.SYSTEM_FREQUENCY(SYSTEM_FREQUENCY),
.frequency(frequency)
) Peak_Mean_inst (
.clk(clk),
.nReset(nReset),
.enable(enable),
.in_signal(in_signal),
.peak_peak(peak_peak),
.mean(mean)
);
// load stimuli
initial begin
$readmemh( "signal.hex", testvec_in_signal );
end
// generate clock
always begin
clk = 1;
forever #5 clk = ~clk; // 100MHz
end
always@( posedge clk ) begin
if( !nReset || (sample_counter == sample-1) ) begin
sample_counter <= 0;
end else begin
++sample_counter;
end
end
// testbench
always@( posedge clk ) begin
if( !nReset ) begin
enable <= 0;
end else begin
case( sample_counter )
0.05*sample-1: enable <= 1;
0.25*sample-1: enable <= 1;
0.45*sample-1: enable <= 1;
0.65*sample-1: enable <= 1;
0.85*sample-1: enable <= 1;
default: enable <= 0;
endcase
end
end
always@( posedge clk ) begin
if( !nReset ) begin
counter_tb_state <= 0;
end else begin
if( enable ) begin
++counter_tb_state;
if( counter_tb_state >= TEST_LEN ) begin
$fclose( f );
$finish;
end
end
end
end
initial begin
nReset <= 0;
repeat(4)@( posedge clk );
nReset <= 1;
f = $fopen( "result.txt", "w" );
$fwrite( f, "Peak_Peak Mean\n" );
end
always@( * ) begin
$fwrite( f, " %h %h\n", peak_peak, mean );
end
endmodule
信号.hex
00000
00000
00000
00000
00000
FFFFF
FFFFF
FFFFF
FFFFF
FFFFF
00000
00F00
200F0
00000
00000
00AAA
1FFFF
77777
20000
08855
2AAAA
3BBBB
24444
27890
35467
10045
03000
09876
14597
19455
00000
00000
ref.txt(参见结果.txt)
Peak_Peak Mean
00000 00000
00000 3ffff
20e10 307f8
3ffff 3ffff
17777 2ffff
16455 0e22a
我在每个
always@( posedge clk )
上设置了一个断点。根据我的理解,每个 always
块应该在正时钟边沿触发一次。奇怪的是,这里的情况并非如此(或至少在我的系统上)。
在最初的几个周期中,正时钟会触发每个
always
块。然后在周期 5 和 6,正时钟边沿不会触发最后两个 always
块(peak_reg 和mean_reg)。之后,正时钟边沿再次触发每个 always
块。
最后两个区块的停工在较大的项目中变得更加严重。最后两个块很少被触发,这会导致不正确的行为。
我理解错了吗? 我的代码有错误吗?
Vivado 中是否有一个设置(我找不到)强制其
always
触发始终在事件处阻塞,以防它们因某种原因在模拟中得到优化?这是 Vivado 错误吗?
这一行:
++counter_freq;
行为如下:
counter_freq = counter_freq + 1;
换句话说,自增运算符 (
++
) 是一个阻塞赋值。
但是,由于您试图描述顺序逻辑,因此应该使用 nonblocking 赋值 (
<=
)。将代码更改为:
counter_freq <= counter_freq + 1;
您在其他 2 个地方使用了
++
。它们也应该改变。
当我进行更改时,我会得到一个不同的
result.txt
文件。但是,我没有使用 Vivado 来运行我的仿真。
请参阅 IEEE Std 1800-2017,第 11.4.2 节 递增和递减运算符:
这些递增和递减赋值运算符表现为阻塞 作业。