You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

VHDL环形计数器:count_en与时钟沿同步下拉时的正确行为实现问询

解决环形计数器与同步count_en的采样竞争问题

首先咱们得把问题根源说透:你的count_en时钟同步信号——控制电路会在时钟上升沿把它拉低,这就导致在预期计数的t=130ns时钟沿,环形计数器刚要采样count_en时,信号刚好被拉低,直接错过计数触发。本质上这是同步控制信号的「脉冲丢失」问题,因为count_en的有效窗口刚好和采样边沿重叠了。

下面给你两种经过工程验证的VHDL实现方案,都能完美解决这个问题:


方案1:边沿检测+计数请求锁存(鲁棒性最强)

这个方案会主动捕捉count_en的上升沿,把它转换成一个持续的计数请求,直到环形计数器完成移位后再清除请求,彻底避免采样竞争。

VHDL代码示例

library IEEE;
use IEEE.STD_LOGIC_1164.ALL;

entity ring_counter is
    Port (
        clk         : in  STD_LOGIC;
        rst_n       : in  STD_LOGIC; -- 低电平全局复位
        enable      : in  STD_LOGIC; -- 计数器全局使能
        count_en    : in  STD_LOGIC; -- 同步计数触发信号
        F0          : out STD_LOGIC;
        F1          : out STD_LOGIC
        -- 更多输出可根据计数器位数扩展
    );
end ring_counter;

architecture Behavioral of ring_counter is
    signal count_reg       : STD_LOGIC_VECTOR(1 downto 0); -- 2位环形计数器寄存器
    signal count_en_r      : STD_LOGIC; -- count_en延迟一拍的寄存器
    signal count_en_edge   : STD_LOGIC; -- count_en上升沿检测信号
    signal count_req       : STD_LOGIC; -- 计数请求锁存信号
begin
    -- 1. 检测count_en的上升沿:哪怕信号被同步拉低,也能捕捉到有效触发
    process(clk, rst_n)
    begin
        if rst_n = '0' then
            count_en_r <= '0';
        elsif rising_edge(clk) then
            count_en_r <= count_en;
        end if;
    end process;
    count_en_edge <= count_en and not count_en_r; -- 上升沿逻辑:当前高+前一拍低

    -- 2. 锁存计数请求:直到计数器响应后再清除
    process(clk, rst_n)
    begin
        if rst_n = '0' then
            count_req <= '0';
        elsif rising_edge(clk) then
            if count_en_edge = '1' then
                count_req <= '1'; -- 捕捉到上升沿,置位请求
            elsif (enable = '1' and count_req = '1') then
                count_req <= '0'; -- 计数器已完成移位,清除请求
            end if;
        end if;
    end process;

    -- 3. 环形计数器核心逻辑
    process(clk, rst_n)
    begin
        if rst_n = '0' then
            count_reg <= "01"; -- 初始状态,可根据需求修改
        elsif rising_edge(clk) then
            if enable = '1' and count_req = '1' then
                count_reg <= count_reg(0) & count_reg(1); -- 右移环形移位,可改成左移
            end if;
        end if;
    end process;

    -- 输出映射
    F0 <= count_reg(0);
    F1 <= count_reg(1);
end Behavioral;

方案说明

  • 上升沿检测:通过延迟一拍count_en,精准捕捉到它从低变高的瞬间,哪怕count_en在同一时钟沿被拉低,也能确认它曾经有效过。
  • 请求锁存:把瞬间的上升沿转换成持续的请求信号,确保计数器在enable有效时一定会响应,绝不会丢失触发指令。

方案2:同步打拍采样(简洁高效)

如果你的count_en是严格的单时钟周期脉冲(即控制电路在某个时钟沿拉高,下一个时钟沿拉低),用这个简洁方案就足够了:

VHDL代码示例

library IEEE;
use IEEE.STD_LOGIC_1164.ALL;

entity ring_counter is
    Port (
        clk         : in  STD_LOGIC;
        rst_n       : in  STD_LOGIC;
        enable      : in  STD_LOGIC;
        count_en    : in  STD_LOGIC;
        F0          : out STD_LOGIC;
        F1          : out STD_LOGIC
    );
end ring_counter;

architecture Behavioral of ring_counter is
    signal count_reg       : STD_LOGIC_VECTOR(1 downto 0);
    signal count_en_sync   : STD_LOGIC; -- 延迟一拍的count_en同步信号
begin
    -- 同步打拍:把count_en延迟一拍,让计数器采样到前一拍的有效电平
    process(clk, rst_n)
    begin
        if rst_n = '0' then
            count_en_sync <= '0';
        elsif rising_edge(clk) then
            count_en_sync <= count_en;
        end if;
    end process;

    -- 环形计数器核心:使用延迟后的同步信号触发
    process(clk, rst_n)
    begin
        if rst_n = '0' then
            count_reg <= "01";
        elsif rising_edge(clk) then
            if enable = '1' and count_en_sync = '1' then
                count_reg <= count_reg(0) & count_reg(1);
            end if;
        end if;
    end process;

    F0 <= count_reg(0);
    F1 <= count_reg(1);
end Behavioral;

方案说明

这个逻辑很简单:通过把count_en延迟一拍,让计数器在t=130ns的时钟沿采样到的是前一拍的count_en高电平(此时控制电路还没拉低它),完美避开了采样竞争。


方案选择建议

  • 如果count_en的有效时长不确定(比如不是严格单周期),优先选方案1,鲁棒性拉满,适合大规模复杂设计。
  • 如果count_en是严格的单时钟周期脉冲,方案2足够用,逻辑更简洁,占用资源更少。

另外实际设计中要注意:

  • 所有同步信号要满足FPGA/ASIC的建立/保持时间要求,两个方案里的同步寄存器已经帮你处理了亚稳态问题。
  • 尽量使用全局同步复位,避免异步复位带来的时序风险。

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

火山引擎 最新活动