You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Verilog中@(posedge clk)实现valid_dat单周期延迟失效问题排查

问题描述

我尝试用@(posedge clk)给valid_dat信号引入单周期延迟,但功能未生效。计数器能正常随每个时钟周期自增1,可valid_delay信号却和valid_dat同步变化,请求协助排查原因。

相关Verilog代码

// Butterfly
// 1. U <- (X + Y MOD) P
// 2. V <- ((X - Y) * W * INV) MOD P

// Macro for number of input data, `width groups = 2 x `width numbers in total 
`define width 4

// Top-Level Design
module BF
#(parameter inv = 256'h8fac2de6599e1a9fa53ddf925918fe6fff2d6e9209dacc5e13f75b68fe75c041,
            M = 256'h73eda753299d7d483339d80809a1d80553bda402fffe5bfeffffffff00000001,
            Mp = 256'h3d443ab0d7bf2839181b2c170004ec0653ba5bfffffe5bfdfffffffeffffffff
    ) 
(input clk, input nrst, input valid_dat, input valid_tw, input[255:0] dat_in, output valid_out, output [255:0] dat_out, output calc_done);

localparam wd = $clog2(`width);
localparam state_rst = 3'b000,
           state_ld = 3'b001,
           state_sub = 3'b010,
           state_mont = 3'b011,
           state_out = 3'b100,
           state_done = 3'b101,
           state_halt = 3'b111;

// Indicators
reg pkt_end;
reg pkt_start, valid_delay;
// For loop index
integer i;
//Buffers
reg [255:0] A_buffer[`width-1:0], B_buffer[`width-1:0], TW_buffer[`width-1:0], U_buffer[`width-1:0], V_buffer[`width-1:0];
// Counters 
reg [wd-1:0] cnt_tw, cnt_calc;
reg [wd:0] cnt_dat, cnt_out;
// Start signals
reg AddStart, SubStart, MontStart;
// Component Inputs
wire [255:0] AddA, AddB, SubA, SubB, MontA, MontB;
// Component Outpus -- Busy/Done signals
wire AddBusy, AddDone, SubBusy, SubDone, MontBusy, MontDone;
// Component Outpus -- Calculation results
wire [255:0] AddC, SubC, MontC;
// Outputs
reg valid_out_s;
reg calc_done_s;
reg [255:0] dat_out_s;
// Control Unit state
reg [2:0] state;

//test 
reg [255:0] dat_test;

// Instantiation of Calculation components
Mont256 #(.inv(inv), .M(M), .Mp(Mp)) mont (.clk(clk), .start(MontStart), .A(MontA), .B(MontB), .busy(MontBusy), .done(MontDone), .C(MontC));
Add256 #(.M(M)) add (.clk(clk), .start(SubStart), .A(AddA), .B(AddB), .busy(AddBusy), .done(AddDone), .C(AddC));
Sub256 #(.M(M)) sub (.clk(clk), .start(SubStart), .A(SubA), .B(SubB), .busy(SubBusy), .done(SubDone), .C(SubC));

// Indicators
always @(posedge clk or negedge nrst) begin
    if (!nrst) begin
        valid_delay <= 0;
    end
    else begin
        valid_delay <= valid_dat;
    end
end

always @(*) begin
    if (state == state_ld && cnt_tw == `width/2 && cnt_dat == `width) begin
        pkt_end <= 1;
    end 
    else begin
        pkt_end <= 0;
    end
end

// Counters
always @(posedge clk or pkt_start) begin
    if (pkt_start) begin
        cnt_dat <= 0;
        cnt_tw <= 0;
        cnt_calc <= 0;
        cnt_out <= 0;
    end
    else begin
        if (valid_dat) begin
            cnt_dat <= cnt_dat + 1;
        end 
        if (valid_tw) begin
            cnt_tw <= cnt_tw + 1;
        end
        if (MontDone) begin
            cnt_calc <= cnt_calc + 1;
        end
        if (state == state_out) begin
            cnt_out <= cnt_out + 1;
        end
    end 
end 

// Input Buffers
always @(cnt_dat or MontDone) begin // Note to readers: this may cause issues to on board testing since we don't know the status of cnt_dat when its not reset
            //                  but it should be working for the simulation.
    if (valid_dat) begin
        if (cnt_dat[0] == 0) begin
            for (i=1;i<`width;i=i+1) begin
                A_buffer[i] <= A_buffer[i-1];
                A_buffer[0] <= dat_in;
            end
        end
        else begin
        if (cnt_dat[0] == 1) begin
            for (i=1; i<`width; i=i+1) begin
                B_buffer[i] <= B_buffer[i-1];
                B_buffer[0] <= dat_in;
            end 
        end
        end
    end
    if (MontDone) begin
        for (i=1;i<`width;i=i+1) begin
            A_buffer[i] <= A_buffer[i-1];
            A_buffer[0] <= '0;
            B_buffer[i] <= B_buffer[i-1];
            B_buffer[0] <= '0;
        end
    end
end

always @(cnt_tw or MontDone) begin // Note to readers: this may cause issues to on board testing since we don't know the status of cnt_tw when its not reset
               //                  but it should be working for the simulation.
    if (valid_tw) begin
        for (i=1; i<`width; i=i+1) begin
            TW_buffer[i] <= TW_buffer[i-1];
            TW_buffer[0] <= dat_in;
        end
    end
    if (MontDone) begin
        for (i=1;i<`width;i=i+1) begin
            TW_buffer[i] <= TW_buffer[i-1];
            TW_buffer[0] <= '0;
        end
    end
end

// Inputs for Adder, Subtractor and Multiplier
assign AddA = A_buffer[`width-1];
assign AddB = B_buffer[`width-1];
assign SubA = A_buffer[`width-1];
assign SubB = B_buffer[`width-1];
assign MontA = SubC;
assign MontB = TW_buffer[`width-1];     

// Control Unit
always @(posedge clk or pkt_start or negedge nrst) begin
    if (!nrst) begin
        valid_out_s <= 0;
        calc_done_s <= 0;
        state <= state_rst;
        AddStart <= 0;
        SubStart <= 0;
        MontStart <= 0;
    end 
    else begin
        if (state == state_rst) begin
            if (pkt_start == 1) begin
                state <= state_ld;
            end 
        end
        if (state == state_ld) begin
            if (pkt_end == 1) begin
                state <= state_sub;
                AddStart <= 1;
                SubStart <= 1;
                MontStart <= 0;
            end
        end
        if (state == state_sub) begin
            if (SubDone == 1) begin
                state <= state_mont;
                SubStart <= 0;
                MontStart <= 1;
            end
        end
        if (state == state_mont) begin
            if (MontDone == 1 && cnt_calc == '1) begin
                state <= state_out;
                SubStart <= 0;
                MontStart <= 0;
                valid_out_s <= 1;
            end
            else begin
                if (MontDone == 1 && cnt_calc != '1) begin
                state <= state_sub;
                SubStart <= 1;
                MontStart <= 0;
                end
                else begin
                    state <= state_mont;
                    SubStart <= 0;
                    MontStart <= 1;
                end 
            end
        end
        if (state == state_out) begin
            if (cnt_out == 2*`width-2) begin
                calc_done_s <= 1;
            end
            if (cnt_out == '1) begin
                state <= state_done;
                calc_done_s <= 0;
                valid_out_s <= 0;
            end
            else begin
                state <= state_out;
            end
        end
        if (state == state_done) begin
            state <= state_done;
        end
    end
end
        
// Output Buffers
always @(AddDone) begin
    if (AddDone) begin
        for (i=1;i<`width;i=i+1) begin
                U_buffer[i] <= U_buffer[i-1];
                U_buffer[0] <= AddC;
            end
    end
end

always @(MontDone) begin
    if (MontDone) begin
        for (i=1;i<`width;i=i+1) begin
                V_buffer[i] <= V_buffer[i-1];
                V_buffer[0] <= MontC;
            end
    end
end

always @(posedge clk) begin
    if (valid_out_s == 1 && cnt_out[0] == 0) begin
        for (i=1;i<`width;i=i+1) begin
            U_buffer[i] <= U_buffer[i-1];
            U_buffer[0] <= '0;
        end
    end
    if (valid_out_s == 1 && cnt_out[0] == 1) begin
        for (i=1;i<`width;i=i+1) begin
            V_buffer[i] <= V_buffer[i-1];
            V_buffer[0] <= '0;
        end
    end
end

// Outuputs
always @(*) begin
    if (valid_out_s == 1 && cnt_out[0] == 0) begin
        dat_out_s <= U_buffer[`width-1];
    end
    else begin 
        if (valid_out_s == 1 && cnt_out[0] == 1) begin
            dat_out_s <= V_buffer[`width-1];
        end
        else begin
            dat_out_s <= '0;
        end
    end
end
assign dat_out = dat_out_s;
assign valid_out = valid_out_s;
assign calc_done = calc_done_s;
    
endmodule

波形图

Waveform


排查分析与解决办法

从代码和波形来看,valid_delay的寄存器逻辑本身是正确的,出现同步问题大概率是以下几个原因:

  1. 仿真采样显示问题
    多数仿真工具默认在时钟上升沿时刻采样信号。如果valid_dat是在时钟上升沿同时变化(比如由组合逻辑生成,或与时钟同步但无严格时序约束),仿真波形会显示valid_delay和valid_dat同步变化,但实际硬件中valid_delay确实是延迟一个周期的。可以检查波形的时间刻度,放大时钟沿附近的细节,看是否存在微小的时序差。

  2. valid_dat的来源问题
    如果valid_dat是上游模块用同一时钟的寄存器输出,那么在当前模块的时钟上升沿,valid_dat的新值刚稳定,valid_delay会在下一个周期捕获到该值。但如果valid_dat是组合逻辑直接驱动,没有经过寄存器同步,就会出现和valid_delay视觉上同步的情况。建议上游模块先将valid_dat打一拍再输出。

  3. 未知状态干扰
    代码中pkt_start是reg类型但未在复位或任何逻辑中初始化,初始值不确定。虽然它和valid_delay没有直接关联,但未知状态可能影响整体仿真环境,建议在复位逻辑中添加pkt_start <= 0;来消除潜在干扰。

  4. 验证与修复步骤

    • 添加二级延迟验证寄存器逻辑:
      reg valid_delay2;
      always @(posedge clk or negedge nrst) begin
          if (!nrst) begin
              valid_delay2 <= 0;
          end
          else begin
              valid_delay2 <= valid_delay;
          end
      end
      
      观察valid_delay2是否比valid_delay延迟一个周期,以此确认寄存器打拍逻辑是否正常。
    • 调整仿真采样设置:将仿真工具的采样时间改为时钟上升沿之后的ns级(比如1ns),查看是否能显示出valid_delay与valid_dat的时序差。
    • 同步valid_dat来源:如果valid_dat是组合逻辑输出,在上游模块用寄存器打一拍后再输入到当前BF模块。

内容的提问来源于stack exchange,提问作者user22714606

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.22 01:37:02