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

可变输出流延迟模块异常:行为仿真正常但综合后失效

脉冲延迟模块综合失效问题排查与修复

需求与问题现状

  • 模块需求:处理脉冲间隔为500-12500 clk周期的数据流,将其延迟指定的500-12500 clk周期,禁止使用12500位移位寄存器。
  • 当前问题:Verilog代码行为仿真正常,但综合后功能完全失效,原理图仅保留输出缓冲与引脚,仿真无输出。
  • 设计思路:用25个14位寄存器存储脉冲间隔,等待指定延迟后按存储信息输出,通过unusedStorage标记已使用的存储项。

原代码

module data_delay_counter(
    input clk,
    input en,
    input [13:0] ShiftNum,
    input s_IN,
    output reg s_OUT
    );
    
    // counting phase
    reg [13:0] cyclesToInputPulse = 0; // counted clk cycles before a pulse
    // storage phase
    reg [4:0] memoryIndxWrite = 0; // index to write pulse information to
    reg [13:0] storage [0:24]; // memory for pulse information
    // delay phase
    reg [13:0] delayCounter = 0; // output delay counter
    // output phase
    reg [4:0] memoryIndxRead = 0; // index to read out pulse information from
    reg [13:0] cyclesToOutputPulse = 0; // counted clk cycles up to number read out from storage
    reg [24:0] unusedStorage = 0; // specifies if storage pulse info has been used or not, 1 for unused 0 for used
      
    always @(posedge clk) begin
        if (en == 1) begin
            // counting phase
            if (s_IN != 1)
                cyclesToInputPulse = cyclesToInputPulse + 14'd1;
            else begin
            // storage phase  
                cyclesToInputPulse = cyclesToInputPulse + 14'd1;
                storage[memoryIndxWrite] = cyclesToInputPulse;

                unusedStorage[memoryIndxWrite] = 1;
                cyclesToInputPulse = 14'd0;
                memoryIndxWrite = memoryIndxWrite + 14'd1;
                if (memoryIndxWrite == 25)
                    memoryIndxWrite = 14'd0;
                
            end
            // delay phase
            if (delayCounter != ShiftNum)
                delayCounter = delayCounter + 1;
            // output phase
            else begin
                cyclesToOutputPulse = cyclesToOutputPulse + 14'd1;
                if (cyclesToOutputPulse == storage[memoryIndxRead] && unusedStorage[memoryIndxRead] == 1) begin
                    s_OUT = 1;
                    unusedStorage[memoryIndxRead] = 0;
                    cyclesToOutputPulse = 14'd0;
                    memoryIndxRead = memoryIndxRead + 14'd1;
                    if (memoryIndxRead == 25)
                        memoryIndxRead = 0;
                end
                else begin
                    s_OUT = 0;
                end
            end
        end
        else begin
            cyclesToInputPulse = 0;
            memoryIndxWrite = 0;
            delayCounter = 0;
            cyclesToOutputPulse = 0;
            memoryIndxRead = 0;
            unusedStorage = 0;
            s_OUT = 0;
        end
    end 
   
    always @(ShiftNum) begin
        cyclesToInputPulse = 0;
        memoryIndxWrite = 0;
        delayCounter = 0;
        cyclesToOutputPulse = 0;
        memoryIndxRead = 0;
        unusedStorage = 0;
        s_OUT = 0;
    end

代码问题分析

  1. 多驱动冲突
    同一个寄存器(如cyclesToInputPulse、memoryIndxWrite)同时被always @(posedge clk)时序块和always @(ShiftNum)组合逻辑块驱动,违反Verilog单驱动规则。综合工具无法处理这种冲突,会直接优化掉相关逻辑,导致功能完全丢失。

  2. 阻塞赋值错误
    时序逻辑块中全部使用阻塞赋值(=),而非标准的非阻塞赋值(<=)。虽然行为仿真可能正常,但综合时会破坏寄存器的同步更新机制,导致逻辑被错误优化或功能异常。

  3. 位宽不匹配与边界错误

    • 5位的memoryIndxWrite/memoryIndxRead自增时使用14位的14'd1,位宽不匹配引发隐式类型转换,可能导致综合工具误判。
    • 地址边界判断memoryIndxWrite == 25错误:0-24共25个存储单元,最大值是24而非25,会导致索引越界。
  4. 存储单元未显式初始化
    数组storage未显式初始化,综合时可能被视为无关项优化,导致存储的数据不可靠。

修复后的代码

module data_delay_counter(
    input clk,
    input en,
    input [13:0] ShiftNum,
    input s_IN,
    output reg s_OUT
    );
    
    // counting phase
    reg [13:0] cyclesToInputPulse = 14'd0; // counted clk cycles before a pulse
    // storage phase
    reg [4:0] memoryIndxWrite = 5'd0; // index to write pulse information to
    reg [13:0] storage [0:24]; // memory for pulse information
    // delay phase
    reg [13:0] delayCounter = 14'd0; // output delay counter
    // output phase
    reg [4:0] memoryIndxRead = 5'd0; // index to read out pulse information from
    reg [13:0] cyclesToOutputPulse = 14'd0; // counted clk cycles up to number read out from storage
    reg [24:0] unusedStorage = 25'd0; // specifies if storage pulse info has been used or not, 1 for unused 0 for used

    // 检测ShiftNum变化实现复位
    reg [13:0] ShiftNum_r = 14'd0;
      
    always @(posedge clk) begin
        // 捕获ShiftNum的上一周期值
        ShiftNum_r <= ShiftNum;

        // 复位逻辑优先:en低电平或ShiftNum变化时复位
        if (!en || (ShiftNum != ShiftNum_r)) begin
            cyclesToInputPulse <= 14'd0;
            memoryIndxWrite <= 5'd0;
            delayCounter <= 14'd0;
            cyclesToOutputPulse <= 14'd0;
            memoryIndxRead <= 5'd0;
            unusedStorage <= 25'd0;
            s_OUT <= 1'b0;
        end else begin
            // counting phase
            if (s_IN != 1'b1) begin
                cyclesToInputPulse <= cyclesToInputPulse + 14'd1;
            end else begin
            // storage phase  
                cyclesToInputPulse <= cyclesToInputPulse + 14'd1;
                storage[memoryIndxWrite] <= cyclesToInputPulse;

                unusedStorage[memoryIndxWrite] <= 1'b1;
                cyclesToInputPulse <= 14'd0;
                // 修正位宽匹配
                memoryIndxWrite <= memoryIndxWrite + 5'd1;
                if (memoryIndxWrite == 5'd24) begin
                    memoryIndxWrite <= 5'd0;
                end
            end
            // delay phase
            if (delayCounter != ShiftNum) begin
                delayCounter <= delayCounter + 14'd1;
            end
            // output phase
            if (delayCounter == ShiftNum) begin
                cyclesToOutputPulse <= cyclesToOutputPulse + 14'd1;
                if (cyclesToOutputPulse == storage[memoryIndxRead] && unusedStorage[memoryIndxRead] == 1'b1) begin
                    s_OUT <= 1'b1;
                    unusedStorage[memoryIndxRead] <= 1'b0;
                    cyclesToOutputPulse <= 14'd0;
                    // 修正位宽匹配
                    memoryIndxRead <= memoryIndxRead + 5'd1;
                    if (memoryIndxRead == 5'd24) begin
                        memoryIndxRead <= 5'd0;
                    end
                end else begin
                    s_OUT <= 1'b0;
                end
            end else begin
                s_OUT <= 1'b0;
            end
        end
    end 
endmodule

修复要点

  • 移除多驱动冲突:删除独立的always @(ShiftNum)组合逻辑块,将ShiftNum变化时的复位逻辑整合到时钟沿的时序块中,通过检测ShiftNum的前后值变化实现复位。
  • 替换为非阻塞赋值:时序逻辑中全部使用<=,确保寄存器同步更新,符合综合工具的要求。
  • 修正位宽与边界:所有自增和比较操作使用匹配的位宽,修正存储单元的索引边界判断(0-24)。
  • 显式初始化:所有寄存器都显式初始化位宽和初始值,避免综合时的不确定优化。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.07 05:45:39