先进先出存储器(FIFO)设计
摘要:同步FIFO设计,包括所有代码,分析,以及仿真结果
FIFO介绍
要求:存入数据按顺序排放,存储器全满时给出信号并拒绝继续存入,全空时也给出信号并拒绝读出;读出时按先进先出原则;存储数据一旦读出就从存储器中消失。
特点
先进先出( First In First Out,FIFO)与普通存储器的区别是没有外部读写地址线,其数据地址由内部读写指针自动加减1完成。
FIFO通常利用双口RAM和读写地址产生模块来实现其功能。
读写指针
FIFO存储器是一个环形结构,读地址计数器和写地址计数器分别代表下一次读数据操作时的读指针和下一次写数据操作时的写指针。
这种环形结构的FIFO的特点是:数据满时,FIFO内的数据个数为总存储单元个数减1,即必须至少有一个数据为空,这是因为,如果在环形结构的FIFO没有留有一个空数据,则无法区分FIFO是空还是满这两种状态。当FIFO复位后,读地址计数器和写地址计数器复位,此时FIFO为空。
当wr_ptr=rd_ptr
时,FIFO数据为空;
当wr_ptr-rd_ptr=M-l
或rd_ptr-wr_ptr=l
时,FIFO数据为满;
当wr_ptr>=rd_ptr
时,wr_ptr-rd_ptr
为FIFO内数据个数;
当wr_ptr<=rd_ptr
时,M-(rd_ptr-wr_ptr)
为FIFO内数据个数。
模块实现
双端口RAM
端口定义
entity dualram is
generic(widthi : positive :=8;
depth : positive :=8);
port(-----port a is only for writing
clka : in STD_LOGIC;
wr : in STD_LOGIC; --写信号有效
addra : in STD_LOGIC_VECTOR(depth-1 downto 0);--写指针
datain : in STD_LOGIC_VECTOR(widthi-1 downto 0);
-----port b is only for reading
clkb : in STD_LOGIC;
rd : in STD_LOGIC; --读信号有效
addrb : in STD_LOGIC_VECTOR(depth-1 downto 0);--读指针
dataout : out STD_LOGIC_VECTOR(widthi-1 downto 0)
);
end dualram;
结构体实现
architecture Behavioral of dualram is
type ram is array(2 **3 downto 0) of STD_LOGIC_VECTOR(widthi-1 downto 0);
signal dualram:ram;
begin
process(clka) --写进程
begin
if clka'event and clka = '1' then
if wr='1' then
dualram(conv_integer(addra))<=datain;
end if;
end if;
end process;
process(clkb) --读进程
begin
if clkb'event and clkb = '1' then
if rd='1' then
dataout<=dualram(conv_integer(addrb));
end if;
end if;
end process;
end Behavioral;
写地址计数器
端口定义
entity write_pointer is
generic(
depth : positive :=8);
port(clk : in STD_LOGIC;
rst : in STD_LOGIC;
wr : in STD_LOGIC;
full : in STD_LOGIC;
wr_pt : out STD_LOGIC_VECTOR(depth-1 downto 0)
);
end write_pointer;
结构体实现
architecture Behavioral of write_pointer is
signal wr_pt_t:STD_LOGIC_VECTOR(depth-1 downto 0);--writer pointer counter
begin
process(clk,rst)
begin
if rst='1' then
wr_pt_t<=(others=>'0');
elsif clk'event and clk = '1' then
if wr='1' and full='0' then
wr_pt_t<=wr_pt_t+1;
end if;
end if;
end process;
wr_pt<=wr_pt_t;
end Behavioral;
读地址计数器
端口定义
entity read_pointer is
generic(
depth : positive :=8);
port(clk : in STD_LOGIC;
rst : in STD_LOGIC;
rq : in STD_LOGIC;
empty : in STD_LOGIC;
rd_pt : out STD_LOGIC_VECTOR(depth-1 downto 0)
);
end read_pointer;
结构体实现
architecture Behavioral of read_pointer is
signal rd_pt_t:STD_LOGIC_VECTOR(depth-1 downto 0);--read_pointer counter
begin
process(clk,rst)
begin
if rst='1' then
rd_pt_t<=(others=>'0');
elsif clk'event and clk = '1' then
if rq='1' and empty='0'then
rd_pt_t<=rd_pt_t+1;
end if;
end if;
end process;
rd_pt<=rd_pt_t;
end Behavioral;
空满状态产生器
端口定义
entity judge_status is
generic(
depth : positive :=8);
port(clk : in STD_LOGIC;
rst : in STD_LOGIC;
wr_pt : in STD_LOGIC_VECTOR(depth-1 downto 0);
rd_pt : in STD_LOGIC_VECTOR(depth-1 downto 0);
empty : out STD_LOGIC;
full : out STD_LOGIC
);
end judge_status;
结构体实现
architecture Behavioral of judge_status is
begin
process(clk,rst)--空状态产生
begin
if rst='1' then
empty<='1';
elsif clk'event and clk = '1' then
if wr_pt=rd_pt then
empty<='1';
else
empty<='0';
end if;
end if;
end process;
process(clk,rst)--满状态产生
begin
if rst='1' then
full<='1';
elsif clk'event and clk = '1' then
if wr_pt>rd_pt then
if (rd_pt+depth)=wr_pt then
full<='1';
else
full<='0';
end if;
else
if (wr_pt+1)=rd_pt then
full<='1';
else
full<='0';
end if;
end if;
end if;
end process;
end Behavioral;
顶层模块(连线)
端口定义
entity fifo_all is
generic(widthi : positive :=8;
depth : positive :=8);
port(-----port a is only for writing
clk : in STD_LOGIC;
rst : in STD_LOGIC;
wr : in STD_LOGIC;
rd : in STD_LOGIC;
datain : in STD_LOGIC_VECTOR(widthi-1 downto 0);
dataout: out STD_LOGIC_VECTOR(widthi-1 downto 0);
empty_out : out STD_LOGIC;
full_out : out STD_LOGIC
);
end fifo_all;
内部实现
signal full,empty : std_logic;
signal rd_pt : STD_LOGIC_VECTOR(depth-1 downto 0);
signal wr_pt : STD_LOGIC_VECTOR(depth-1 downto 0);
begin
judge_status0: judge_status port map(clk,rst,wr_pt,rd_pt,empty,full);
dualram0: dualram port map(clk,wr,wr_pt,datain,clk,rd,rd_pt,dataout);
read_pointer0:read_pointer port map(clk,rst,rd,empty,rd_pt);
write_pointer0:write_pointer port map(clk,rst,wr,full,wr_pt);
empty_out<=empty;
full_out<=full;
仿真结果
可以看到,写进去的数据被依次读取除来了;
而以上代码有错误,错误在空间写满后如果wr
信号如果有效,会覆盖上次写的数据,而事实上此次写不应该有效。修改方法是在双端口rom
里引入empty
,full
信号判断作为是否写和读的条件判断。
本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 不听话的兔子君!