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;
问题根源分析
RAM写入无保护机制
当前PROC_RAM进程在每个时钟上升沿无条件执行ram(head) <= wr_data,即使缓冲区已满(full_i='1')或写使能无效(wr_en='0'),这会直接覆盖head指向的未读数据,是数据被覆盖的核心原因。满状态判断逻辑错误
环形缓冲区的标准满状态应为(head + 1) mod RAM_DEPTH = tail,但当前通过fill_count_i >= RAM_DEPTH - 1判断满状态,结合fill_count_i的范围限制(最大为RAM_DEPTH-1),导致:- 实际可用容量减少1(仅能存储
RAM_DEPTH-1个数据) - 满状态判断时机不准确,无法有效阻止非法写入
- 实际可用容量减少1(仅能存储
读指针循环行为被误判
读指针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
相关产品推荐
相关产品推荐

