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

VHDL实现ADC串口采集FSM中check_count状态时长异常求助

VHDL 24位ADC串口采集FSM状态异常问题排查

我是VHDL新手,要实现ADC的24位串口数据采集功能:该串口包含24位串行数据线路、新数据就绪指示DRDY信号,以及上升沿推送数据的串行时钟SCLK,系统持续运行。需求是准确采集24位采样数据,转换为并行总线输出,同时给后续模块提供“数据有效”信号。因为系统时钟是串口时钟的4倍,我用有限状态机(FSM)实现,状态包括init、wait_drdy、wait_sclk、inc_count、check_count,通过计数器cnt判断是否完成24位采集。

但行为仿真中发现check_count状态的持续时间远超过预期的1个系统时钟周期,导致错过后续SCLK脉冲,无法采集下一位数据。我尝试把计数器cnt改成FSM进程内部的整数变量,问题依旧,求指点原因。

FSM及信号图

FSM及信号波形图

VHDL代码

library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.NUMERIC_STD.ALL;


entity serial_ads1675 is
    Port ( 
            clk     : in STD_LOGIC;
            reset   : in STD_LOGIC;
            sclk    : in std_logic;
            sdata   : in std_logic;
            drdy    : in std_logic;
            pdata   : out std_logic_vector(23 downto 0);
            pdready : out std_logic
    );
end serial_ads1675;

architecture Behavioral of serial_ads1675 is

-- Internal declarations
signal ipdata : std_logic_vector (23 downto 0);
signal ipdready : std_logic;
signal tmp1, tmp2, tmp3, tmp4 : std_logic;
signal rise_drdy, rise_sclk : std_logic;
signal cnt : unsigned (4 downto 0);

type state is (init, wait_drdy, wait_sclk, inc_count, check_count);
signal actual_state, next_state : state;

begin

-- Concurrent statements
pdata <= ipdata;
pdready <= ipdready;
rise_drdy <= '1' when ((tmp1 = '1') and (tmp2 = '0')) else '0';
rise_sclk <= '1' when ((tmp3 = '1') and (tmp4 = '0')) else '0';

-- Process for edge detection
process (clk, reset)
begin
    if(reset = '0') then
        tmp1 <= '0';
        tmp2 <= '0';
        tmp3 <= '0';
        tmp4 <= '0';
    elsif (falling_edge(clk)) then
        tmp1 <= drdy;
        tmp2 <= tmp1;
        tmp3 <= sclk;
        tmp4 <= tmp3;
    end if;
end process;

-- State register process
process (reset, clk)
begin
    if (reset = '0') then
        actual_state <= init;
    elsif (rising_edge(clk)) then
        actual_state <= next_state;
    end if;
end process;

-- Next State affectation process
process (rise_sclk, rise_drdy)
begin
    case actual_state is
    
        when init =>
            next_state <= wait_drdy;
            ipdata <= (others => '0');
            ipdready <= '0';
            cnt <= (others => '0');
            
        when wait_drdy =>
            if (rise_drdy = '0') then
                next_state <= actual_state;
            else
                next_state <= wait_sclk;
            end if;
            cnt <= (others => '0');
            
        when wait_sclk =>
            if (rise_sclk = '0') then
                next_state <= actual_state;
            else
                next_state <= inc_count;
            end if;
            ipdready <= '0';
                        
        when inc_count =>
            next_state <= check_count;
            cnt <= cnt + 1;
            ipdready <= '0';
            ipdata(23 downto 1) <= ipdata(22 downto 0);
            ipdata(0) <= sdata;
            
        when check_count =>
            case cnt is
                when "11000" =>
                    next_state <= wait_drdy;
                    ipdready <= '1';
                when others =>
                    next_state <= wait_sclk;
                    ipdready <= '0';
            end case;
                        
        when others =>
            next_state <= init;
    end case;
end process;

end Behavioral;

行为仿真波形图

行为仿真波形图


问题原因分析

  1. 敏感列表不完整:下一状态进程的敏感列表仅包含rise_sclk和rise_drdy,但进程中使用了actual_state和cnt信号。这两个信号的变化无法触发进程更新,导致check_count状态下,只有当rise_sclk或rise_drdy变化时才会执行状态判断,而多数时间进程不会响应,状态长时间停滞。
  2. 组合逻辑驱动寄存器违规:在组合逻辑进程中直接驱动cnt、ipdata、ipdready等寄存器类型信号,会引发竞争冒险,且不符合同步设计规范。这类信号必须在时钟边沿触发的时序进程中更新。
  3. 计数器与状态不同步:cnt在组合逻辑中更新,没有与系统时钟同步,导致计数器值变化滞后于状态转换,进一步加剧状态判断的延迟。

修正方案

  • 补全敏感列表:将下一状态进程的敏感列表改为actual_state, rise_sclk, rise_drdy, cnt,确保所有影响状态转换的信号变化都能触发进程。
  • 分离组合与时序逻辑:仅在组合逻辑中计算next_state,将cnt、ipdata、ipdready的更新移到时钟触发的时序进程中,保证同步操作。
  • 同步计数器更新:将cnt的增量操作放到时序进程内,与状态转换同步,确保每个系统时钟周期仅更新一次。

修正后的核心代码片段:

-- 组合逻辑:仅计算next_state
process (actual_state, rise_sclk, rise_drdy, cnt)
begin
    next_state <= actual_state; -- 默认保持当前状态
    case actual_state is
        when init =>
            next_state <= wait_drdy;
        when wait_drdy =>
            if rise_drdy = '1' then
                next_state <= wait_sclk;
            end if;
        when wait_sclk =>
            if rise_sclk = '1' then
                next_state <= inc_count;
            end if;
        when inc_count =>
            next_state <= check_count;
        when check_count =>
            if cnt = "11000" then
                next_state <= wait_drdy;
            else
                next_state <= wait_sclk;
            end if;
        when others =>
            next_state <= init;
    end case;
end process;

-- 时序逻辑:更新所有寄存器
process (clk, reset)
begin
    if reset = '0' then
        actual_state <= init;
        ipdata <= (others => '0');
        ipdready <= '0';
        cnt <= (others => '0');
    elsif rising_edge(clk) then
        actual_state <= next_state;
        case actual_state is
            when init =>
                ipdata <= (others => '0');
                ipdready <= '0';
                cnt <= (others => '0');
            when wait_drdy =>
                if rise_drdy = '1' then
                    cnt <= (others => '0');
                end if;
            when inc_count =>
                cnt <= cnt + 1;
                ipdata <= ipdata(22 downto 0) & sdata; -- 左移加载新数据
                ipdready <= '0';
            when check_count =>
                ipdready <= '1' when cnt = "11000" else '0';
            when others =>
                null;
        end case;
    end if;
end process;

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.09 13:50:24