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

VHDL环形缓冲区读指针异常前移问题求助

环形缓冲区指针异常与数据覆盖问题排查解决

问题描述

初始化后以25MHz频率写入数据时,读指针本该停在内存地址511(对应RAM_DEPTH值),却意外前移至0,导致首个已读取的数据被覆盖。尝试将PROC_HEAD进程中的判断条件从full_i改为full_next,问题依旧存在。

问题代码

library ieee;
use ieee.std_logic_1164.all;
 
entity ring_buffer is
  generic (
    RAM_WIDTH : natural;
    RAM_DEPTH : natural
  );
  port (
    clk : in std_logic;
    rst : in std_logic;
 
    -- Write port
    wr_en : in std_logic;
    wr_data : in std_logic_vector(RAM_WIDTH - 1 downto 0);
 
    -- Read port
    rd_en : in std_logic;
    rd_valid : out std_logic;
    rd_data : out std_logic_vector(RAM_WIDTH - 1 downto 0);
 
    -- Flags
    empty : out std_logic;
    empty_next : out std_logic;
    full : out std_logic;
    full_next : out std_logic;
 
    -- The number of elements in the FIFO
    fill_count : out integer range RAM_DEPTH - 1 downto 0
  );
end ring_buffer;
 
architecture rtl of ring_buffer is
 
  type ram_type is array (0 to RAM_DEPTH - 1) of
    std_logic_vector(wr_data'range);
  signal ram : ram_type;
 
  subtype index_type is integer range ram_type'range;
  signal head : index_type;
  signal tail : index_type;
 
  signal empty_i : std_logic;
  signal full_i : std_logic;
  signal fill_count_i : integer range RAM_DEPTH - 1 downto 0;
 
  -- Increment and wrap
  procedure incr(signal index : inout index_type) is
  begin
    if index = index_type'high then
      index <= index_type'low;
    else
      index <= index + 1;
    end if;
  end procedure;
 
begin
 
  -- Copy internal signals to output
  empty <= empty_i;
  full <= full_i;
  fill_count <= fill_count_i;
 
  -- Set the flags
  empty_i <= '1' when fill_count_i = 0 else '0';
  empty_next <= '1' when fill_count_i <= 1 else '0';
  full_i <= '1' when fill_count_i >= RAM_DEPTH - 1 else '0';
  full_next <= '1' when fill_count_i >= RAM_DEPTH - 2 else '0';
 
  -- Update the head pointer in write
  PROC_HEAD : process(clk)
  begin
    if rising_edge(clk) then
      if rst = '1' then
        head <= 0;
      else
 
        if wr_en = '1' and full_i = '0' then
          incr(head);
        end if;
 
      end if;
    end if;
  end process;
 
  -- Update the tail pointer on read and pulse valid
  PROC_TAIL : process(clk)
  begin
    if rising_edge(clk) then
      if rst = '1' then
        tail <= 0;
        rd_valid <= '0';
      else
        rd_valid <= '0';
 
        if rd_en = '1' and empty_i = '0' then
          incr(tail);
          rd_valid <= '1';
        end if;
 
      end if;
    end if;
  end process;
 
  -- Write to and read from the RAM
  PROC_RAM : process(clk)
  begin
    if rising_edge(clk) then
      ram(head) <= wr_data;
      rd_data <= ram(tail);
    end if;
  end process;
 
  -- Update the fill count
  PROC_COUNT : process(head, tail)
  begin
    if head < tail then
      fill_count_i <= head - tail + RAM_DEPTH;
    else
      fill_count_i <= head - tail;
    end if;
  end process;
 
end architecture;

问题根源分析

  1. RAM写入无保护机制
    当前PROC_RAM进程在每个时钟上升沿无条件执行ram(head) <= wr_data,即使缓冲区已满(full_i='1')或写使能无效(wr_en='0'),这会直接覆盖head指向的未读数据,是数据被覆盖的核心原因。

  2. 满状态判断逻辑错误
    环形缓冲区的标准满状态应为(head + 1) mod RAM_DEPTH = tail,但当前通过fill_count_i >= RAM_DEPTH - 1判断满状态,结合fill_count_i的范围限制(最大为RAM_DEPTH-1),导致:

    • 实际可用容量减少1(仅能存储RAM_DEPTH-1个数据)
    • 满状态判断时机不准确,无法有效阻止非法写入
  3. 读指针循环行为被误判
    读指针tail从511(RAM_DEPTH-1)递增到0是正常的环形循环逻辑,但由于RAM无保护写入导致head非法覆盖数据,才引发了用户感知的异常。

修复方案

1. 给RAM写入添加保护条件

修改PROC_RAM进程,仅在写使能有效且缓冲区未满时执行写入操作:

PROC_RAM : process(clk)
begin
  if rising_edge(clk) then
    -- 仅允许在可写入时更新RAM
    if wr_en = '1' and full_i = '0' then
      ram(head) <= wr_data;
    end if;
    -- 读操作始终执行(空状态下读无效数据,但rd_valid会置0)
    rd_data <= ram(tail);
  end if;
end process;

2. 修正满状态与计数逻辑

  • 调整fill_count_i的范围,使其能表示完整的缓冲区容量:
    signal fill_count_i : integer range 0 to RAM_DEPTH;
    
  • 修正计数计算逻辑:
    PROC_COUNT : process(head, tail)
    begin
      if head >= tail then
        fill_count_i <= head - tail;
      else
        fill_count_i <= head - tail + RAM_DEPTH;
      end if;
    end process;
    
  • 修正状态标志判断:
    -- Set the flags
    empty_i <= '1' when fill_count_i = 0 else '0';
    empty_next <= '1' when fill_count_i <= 1 else '0';
    full_i <= '1' when fill_count_i = RAM_DEPTH else '0';
    full_next <= '1' when fill_count_i >= RAM_DEPTH - 1 else '0';
    
  • 同步更新输出端口的fill_count范围:
    -- 修改实体中的fill_count端口定义
    fill_count : out integer range 0 to RAM_DEPTH
    

3. 验证指针移动逻辑

PROC_HEAD和PROC_TAIL的移动条件本身逻辑正确,无需修改。读指针从511循环到0是环形缓冲区的正常行为,只要RAM写入被正确保护,就不会出现数据覆盖问题。

完整修复后代码

library ieee;
use ieee.std_logic_1164.all;
 
entity ring_buffer is
  generic (
    RAM_WIDTH : natural;
    RAM_DEPTH : natural
  );
  port (
    clk : in std_logic;
    rst : in std_logic;
 
    -- Write port
    wr_en : in std_logic;
    wr_data : in std_logic_vector(RAM_WIDTH - 1 downto 0);
 
    -- Read port
    rd_en : in std_logic;
    rd_valid : out std_logic;
    rd_data : out std_logic_vector(RAM_WIDTH - 1 downto 0);
 
    -- Flags
    empty : out std_logic;
    empty_next : out std_logic;
    full : out std_logic;
    full_next : out std_logic;
 
    -- The number of elements in the FIFO
    fill_count : out integer range 0 to RAM_DEPTH
  );
end ring_buffer;
 
architecture rtl of ring_buffer is
 
  type ram_type is array (0 to RAM_DEPTH - 1) of
    std_logic_vector(wr_data'range);
  signal ram : ram_type;
 
  subtype index_type is integer range ram_type'range;
  signal head : index_type;
  signal tail : index_type;
 
  signal empty_i : std_logic;
  signal full_i : std_logic;
  signal fill_count_i : integer range 0 to RAM_DEPTH;
 
  -- Increment and wrap
  procedure incr(signal index : inout index_type) is
  begin
    if index = index_type'high then
      index <= index_type'low;
    else
      index <= index + 1;
    end if;
  end procedure;
 
begin
 
  -- Copy internal signals to output
  empty <= empty_i;
  full <= full_i;
  fill_count <= fill_count_i;
 
  -- Set the flags
  empty_i <= '1' when fill_count_i = 0 else '0';
  empty_next <= '1' when fill_count_i <= 1 else '0';
  full_i <= '1' when fill_count_i = RAM_DEPTH else '0';
  full_next <= '1' when fill_count_i >= RAM_DEPTH - 1 else '0';
 
  -- Update the head pointer in write
  PROC_HEAD : process(clk)
  begin
    if rising_edge(clk) then
      if rst = '1' then
        head <= 0;
      else
        if wr_en = '1' and full_i = '0' then
          incr(head);
        end if;
      end if;
    end if;
  end process;
 
  -- Update the tail pointer on read and pulse valid
  PROC_TAIL : process(clk)
  begin
    if rising_edge(clk) then
      if rst = '1' then
        tail <= 0;
        rd_valid <= '0';
      else
        rd_valid <= '0';
        if rd_en = '1' and empty_i = '0' then
          incr(tail);
          rd_valid <= '1';
        end if;
      end if;
    end if;
  end process;
 
  -- Write to and read from the RAM
  PROC_RAM : process(clk)
  begin
    if rising_edge(clk) then
      if wr_en = '1' and full_i = '0' then
        ram(head) <= wr_data;
      end if;
      rd_data <= ram(tail);
    end if;
  end process;
 
  -- Update the fill count
  PROC_COUNT : process(head, tail)
  begin
    if head >= tail then
      fill_count_i <= head - tail;
    else
      fill_count_i <= head - tail + RAM_DEPTH;
    end if;
  end process;
 
end architecture;

内容的提问来源于stack exchange,提问作者Majora-NK

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.29 08:53:09