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

Verilog预设倒计时计时器初始启动值错误,后续运行正常问题求助

倒计时计时器首次启动异常问题排查与修复

问题现象

基于Verilog开发预设计数器倒计时计时器时,出现首次启动计数异常:

  • 设置1分钟,首次启动从1分59秒开始倒计时
  • 设置10分钟,首次启动从19分59秒开始倒计时
  • 设置1小时,首次启动从1小时59分开始倒计时
    后续循环运行完全正常,初步怀疑问题出在预设值加载或初始化逻辑。

相关代码

计时器主模块

//Timer Module
module timer(KEY,SW,oCNT0,oCNT1,oCNT2,oCNT3,oCNT4,oCNT5,CLOCK_50,Done);

input [1:0] KEY; 
input [9:0] SW;
input      CLOCK_50;


output [3:0] oCNT0,oCNT1,oCNT2,oCNT3,oCNT4,oCNT5;
output wire Done;

wire EN0,EN1,EN2,EN3,EN4,EN5;
wire [3:0] oCNT0, oCNT1,oCNT2,oCNT3,oCNT4,oCNT5;
wire [18:0] oCNT;

assign Done = ({oCNT5,oCNT4,oCNT3,oCNT2,oCNT1,oCNT0}==(24'b000000000000000000000000)) ? 1'b0 : 1'b1;   

assign iRSTn=(SW[7]&~SW[6]&~KEY[0]) ? 1'b0 : 1'b1;  

reg start;

reg [26:0] press_counter;
reg long_press;

reg button_pressed;

//This part of the code implements logic to perform two different functions using a single key.
always @(posedge CLOCK_50 or negedge iRSTn) begin
    if (!iRSTn) begin
        press_counter <= 0;
        button_pressed <= 0;
        long_press <= 0;
        start <= 0;
    end else begin
        if (KEY[1]) begin
            press_counter <= press_counter + 1;
            button_pressed <= 1;

            // 2sec over
            if (press_counter >27'd100000000) begin
                long_press <= 1;
            end
        end else if (button_pressed) begin
            if (long_press) begin
                start <= start; // Preset
            end else begin
                start <= ~start; //Toggle
            end
            // reset
            press_counter <= 0;
            button_pressed <= 0;
            long_press <= 0;
        end
    end
end


COUNTER_LAB
#(.n(23),.k(5000000))
U_Counter_ms_gen(
        .iRSTn(iRSTn),
        .iCLK(CLOCK_50),
        .iEN(start&Done),
        .oEN(EN0),
        .oCNT(oCNT)
); // 5M Count

COUNTER_PRESET_DOWN
#(.n(4),.k(10))
COUNTER_timer_ms(.iCLK(CLOCK_50), .iRSTn(iRSTn), .iPRSTn(1'b0), .iEN(EN0), .iDATA_Preset(4'b0000), .oEN(EN1), .oCNT(oCNT0));  // ms

COUNTER_PRESET_DOWN
#(.n(4),.k(10))
COUNTER_timer_s0(.iCLK(CLOCK_50), .iRSTn(iRSTn), .iPRSTn(1'b0), .iEN(EN1), .iDATA_Preset(4'b0000), .oEN(EN2), .oCNT(oCNT1));  // seconds (units)

COUNTER_PRESET_DOWN
#(.n(3),.k(6))
COUNTER_timer_s1(.iCLK(CLOCK_50), .iRSTn(iRSTn), .iPRSTn((~KEY[1] & ~long_press) & (SW[5:4] == 2'b00) & (SW[7:6] == 2'b10)), .iEN(EN2), .iDATA_Preset(SW[3:0]), .oEN(EN3), .oCNT(oCNT2));  // seconds (tens)

COUNTER_PRESET_DOWN
#(.n(4),.k(10))
COUNTER_timer_m0(.iCLK(CLOCK_50), .iRSTn(iRSTn), .iPRSTn((~KEY[1] & ~long_press) & (SW[5:4] == 2'b01) & (SW[7:6] == 2'b10)), .iEN(EN3), .iDATA_Preset(SW[3:0]), .oEN(EN4), .oCNT(oCNT3));  // minutes (units)

COUNTER_PRESET_DOWN
#(.n(3),.k(6))
COUNTER_timer_m1(.iCLK(CLOCK_50), .iRSTn(iRSTn), .iPRSTn((~KEY[1] & ~long_press) & (SW[5:4] == 2'b10) & (SW[7:6] == 2'b10)), .iEN(EN4), .iDATA_Preset(SW[2:0]), .oEN(EN5), .oCNT(oCNT4));  // minutes (tens)

COUNTER_PRESET_DOWN
#(.n(4),.k(13))
COUNTER_timer_h(.iCLK(CLOCK_50), .iRSTn(iRSTn), .iPRSTn((~KEY[1] & ~long_press) & (SW[5:4] == 2'b11) & (SW[7:6] == 2'b10)), .iEN(EN5), .iDATA_Preset(SW[3:0]), .oEN(), .oCNT(oCNT5));  // hours

endmodule

预设计数器模块

//Timer Counter
module COUNTER_PRESET_DOWN(
    iCLK,
    iRSTn,
    iPRSTn,
    iEN,
    iDATA_Preset,
    oEN, //roll-over signal
    oCNT
);

parameter n=4;
parameter k=10;

input iCLK,iRSTn,iPRSTn,iEN;
input [n-1:0] iDATA_Preset;
output [n-1:0] oCNT;
output oEN;

reg [n-1:0] oCNT;

assign oEN =((oCNT == 0) && (iEN == 1'b1)) ? 1'b1 : 1'b0;

always@(posedge iCLK or negedge iRSTn)
begin
    if(~iRSTn)
    begin
        oCNT <= 0;
    end
    else if(iPRSTn)
    begin 
        oCNT <= iDATA_Preset;
    end
    else if(iEN)
    begin
        if(oCNT==0)
        begin
            oCNT<=k-1;
        end
        else
        begin
            oCNT<=oCNT-1;
        end
    end
    else
    begin 
        oCNT<=oCNT;
    end
end

endmodule

问题根因

  1. 计数器初始化与计数逻辑冲突:COUNTER_PRESET_DOWN复位后初始值为0,当iEN有效时,若oCNT==0会直接加载k-1并触发进位信号oEN。首次启动时,毫秒、秒等低位计数器初始为0,启动后立刻触发进位,导致高位计数器被额外减1次(表现为高位多了一个单位的计数,比如1分钟变成1分59秒=79秒,相当于2分钟减1秒)。
  2. Done信号逻辑导致启动条件异常:Done在计数全0时输出0,否则输出1,启动条件为start&Done。首次启动时,低位计数器初始为0,但整体计数不全0(高位有预设值),Done为1,触发计数后低位立刻触发进位。

修复方案

1. 修正COUNTER_PRESET_DOWN计数逻辑

将计数器的进位触发时机调整为减到0之后,避免初始0直接触发进位:

// 修改后的COUNTER_PRESET_DOWN模块
module COUNTER_PRESET_DOWN(
    iCLK,
    iRSTn,
    iPRSTn,
    iEN,
    iDATA_Preset,
    oEN, //roll-over signal
    oCNT
);

parameter n=4;
parameter k=10;

input iCLK,iRSTn,iPRSTn,iEN;
input [n-1:0] iDATA_Preset;
output reg [n-1:0] oCNT;
output reg oEN;

always@(posedge iCLK or negedge iRSTn)
begin
    if(~iRSTn)
    begin
        oCNT <= 0;
        oEN <= 1'b0;
    end
    else if(iPRSTn)
    begin 
        oCNT <= iDATA_Preset;
        oEN <= 1'b0;
    end
    else if(iEN)
    begin
        if(oCNT == 0)
        begin
            // 只有当计数器已经到0,且允许计数时,才加载k-1并触发进位
            oCNT <= k-1;
            oEN <= 1'b1;
        end
        else
        begin
            oCNT <= oCNT - 1'b1;
            oEN <= 1'b0;
        end
    end
    else
    begin 
        oCNT <= oCNT;
        oEN <= 1'b0;
    end
end

endmodule

核心调整:将oEN改为时序逻辑输出,避免组合逻辑毛刺;只有当计数器已经减到0且iEN有效时,才加载k-1并触发进位,而非初始0就触发。

2. 优化启动前的预设逻辑

确保在首次启动前,所有计数器都加载正确的初始值,比如给毫秒、秒个位/十位模块添加预设触发条件,在启动前将其预设为0:

// 修改毫秒计数器的iPRSTn,在启动前触发预设
COUNTER_PRESET_DOWN
#(.n(4),.k(10))
COUNTER_timer_ms(.iCLK(CLOCK_50), .iRSTn(iRSTn), 
.iPRSTn((~KEY[1] & ~long_press) & (SW[7:6] == 2'b10)), // 新增预设触发
.iEN(EN0), .iDATA_Preset(4'b0000), .oEN(EN1), .oCNT(oCNT0));  // ms

// 同理修改秒个位计数器
COUNTER_PRESET_DOWN
#(.n(4),.k(10))
COUNTER_timer_s0(.iCLK(CLOCK_50), .iRSTn(iRSTn), 
.iPRSTn((~KEY[1] & ~long_press) & (SW[7:6] == 2'b10)), // 新增预设触发
.iEN(EN1), .iDATA_Preset(4'b0000), .oEN(EN2), .oCNT(oCNT1));  // seconds (units)

这样在设置时间后短按按键时,低位计数器也会被预设为0,避免初始0触发进位。

3. 验证Done信号逻辑

确认Done信号逻辑正确,确保只有当计数未到0时才允许计数:

// 保持原逻辑即可,注释说明更清晰
assign Done = ({oCNT5,oCNT4,oCNT3,oCNT2,oCNT1,oCNT0} != 24'd0) ? 1'b1 : 1'b0;

验证方法

  1. 复位后设置1分钟,短按启动,观察是否从1分00秒开始倒计时
  2. 设置10分钟,启动后检查是否从10分00秒开始
  3. 多次启停,确认首次启动异常不再出现

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.17 06:52:33