systemverilog 始终阻止并不总是在事件时触发

问题描述 投票:0回答:1

我正在使用 Xilinx Vivado 2023.2(在 Windows 11 上)。以下代码应该计算输入信号的峰峰值和平均值。 由于未知原因,Vivado 模拟器有时会跳过(Peak_Mean.sv 的)最后两个始终块(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 )
上设置了一个断点。根据我的理解,每个始终块应该在正时钟边沿触发一次。奇怪的是,这里的情况并非如此(或至少在我的系统上)。

在最初的几个周期中,正时钟触发每个总是阻塞。然后在周期 5 和 6,正时钟边沿不会触发最后两个始终块(peak_reg 和mean_reg)。之后,正时钟边沿再次触发每个始终块。

最后两个区块的停工在较大的项目中变得更加严重。最后两个块很少被触发,这会导致不正确的行为。

我理解错了吗? 我的代码有错误吗?

Vivado 中是否有一个设置(我找不到)强制它始终在事件中触发始终块,以防它们因某种原因在模拟中进行优化?这是 Vivado 错误吗?

system-verilog vivado
1个回答
0
投票

这一行:

     ++counter_freq;

行为如下:

     counter_freq = counter_freq + 1;

换句话说,自增运算符 (

++
) 是一个阻塞赋值。

但是,由于您试图描述顺序逻辑,因此应该使用 nonblocking 赋值 (

<=
)。将代码更改为:

     counter_freq <= counter_freq + 1;

您在其他 2 个地方使用了

++
。它们也应该改变。

当我进行更改时,我会得到一个不同的

result.txt
文件。

请参阅 IEEE Std 1800-2017,第 11.4.2 节 递增和递减运算符

这些递增和递减赋值运算符表现为阻塞 作业。

© www.soinside.com 2019 - 2024. All rights reserved.