一、项目概况

名称:同步FIFO设计与验证
预估设计时间:5天
预估占用逻辑单元数量:1100
FPGA芯片型号:Cyclone IV E EP4CE6E22C8

二、设计思想

  项目目标为8位同步FIFO,深度为16。因为标志计数器要计数16,所以标志计数器要定义5位以存储17个数字。

  将项目划分为4个主要模块:RAM寄存器模块、读指针控制模块、写指针控制模块、空满标志控制模块。

  同时划分5项主要功能:读操作、写操作、更新读地址、更新写地址、更新标志位。

  读操作首先判断当前是否是系统时钟的上升沿或复位信号的下降沿,如果不是则不做任何操作,如果是再判断系统复位信号是否有效,若有效则输出0,若无效则再判断读使能信号是否有效且FIFO是否为空,如果满足条件则将当前地址的数据输出,否则不做任何操作。

  写操作首先判断系统时钟是否为上升沿,如果不是则不做操作,如果是再判断写使能信号是否有效且FIFO非满,若满足条件则将数据写入对应写地址,否则不做操作。

  更新读地址操作首先判断是否为时钟信号上升沿或复位信号下降沿,如果不是则不做操作,如果是再判断系统复位信号是否有效,若是则将读地址置为0,若不是再判断读使能信号是否有效且FIFO非空,如果不满足条件则不做操作,否则将读地址加1。

  更新写地址操作首先判断是否为时钟信号上升沿或复位信号下降沿,如果不是则不做操作,如果是再判断系统复位信号是否有效,若是则将写地址置为0,若不是再判断写使能信号是否有效且FIFO非满,若果不满足条件则不做操作,否则将写地址加1。

  更新标志位操作首先判断是否为时钟信号上升沿或复位信号下降沿,如果不是则不做操作,如果是再判断系统复位信号是否有效,若是则将标志计数器置为0,否则对标志计数器进行操作:不可读不可写或可读可写时标志计数器不变,不可读可写时标志计数器加1,可读不可写时标志计数器减1;然后根据标志计数器数值对空满标志进行操作:标志计数器为0时将空标志置为1,标志计数器为16时将满标志置为1,否则空满标志都为0;然后将空满标志输出。

三、项目流程图

读操作:
fifo1
写操作:
fifo2
更新读地址:
fifo3
更新写地址:
fifo4
更新标志位:
fifo5

四、项目实现代码

注:设计块已修改为可上板综合的代码,若进行仿真请酌量修改。

/***************************Copyright (c)***************************
**                         QSTSKING
**
**----------------------------File Info-----------------------------
** File Name:                   fifo.v
** Last Modified Date:          2018-4-25
** Last Modified Version:       1.0
** Description:             xxx
**------------------------------------------------------------------
** Created By:              ArronTY
** Created Date:                2018-4-25
** Version:                 1.0
** Description:             The origin version
**------------------------------------------------------------------
** Modified By:
** Modified Date:
** Modified Version:
** Description:
**------------------------------------------------------------------
*******************************************************************/
module ram(input clk,
              input rst,
              input wr_en,
              input rd_en,
              input [3:0]wr_addr,
              input [3:0]rd_addr,
              input full,
              input empty,
              input [7:0]data_in,
              output reg[7:0]data_out);
              
reg [7:0]ram[0:16];
  
always @(posedge clk or negedge rst)
begin
    if(!rst)
    begin
        data_out<=0;
    end
    else
    begin
        if(rd_en==1 && empty==0)
            data_out<=ram[rd_addr];
    end
end

always @(posedge clk)
begin
    if(wr_en==1 && full==0)
    begin
        ram[wr_addr]<=data_in;
    end
end
endmodule


module wr_addr_gen(input clk, 
                   input rst, 
                   input wr_en, 
                   input full, 
                   output reg [3:0]wr_addr); 
                         
always @(posedge clk or negedge rst) 
begin 
    if(!rst) 
    begin 
        wr_addr<=0; 
    end 
    else if(full==0 && wr_en==1) 
    begin 
        wr_addr<=wr_addr+1;
    end 
    else 
    begin 
        wr_addr<=0;
    end 
end         
endmodule


module rd_addr_gen(input clk,
                   input rst,
                   input rd_en,
                   input empty,
                   output reg[3:0]rd_addr);
                         
always @(posedge clk or negedge rst)
begin
    if(!rst)
    begin
        rd_addr<=0;
    end  
    else
    begin
        if(empty==0&&rd_en==1)
        begin
            rd_addr<=rd_addr+1;
        end
    end
end
endmodule


module flag_gen(input clk,
                     input rst,
                     input wr_en,
                     input rd_en,
                     output reg full,
                     output reg empty);
                     
reg [4:0]count; 



always @(posedge clk or negedge rst)
begin
    if(!rst)
    begin
        count=0;
    end
    else
    begin
        case({wr_en,rd_en})
        2'b00:count<=count;
        2'b01:if(count!=5'b00000)
            count <= count-1;
        2'b10:if(count!=5'b01111)
            count <= count+1;
        2'b11:count<=count;
        endcase      
    end
end

always @(count)
begin
    if(count==5'b10000)
        full<=1;
    else
        full<=0;
end

always @(count)
begin
    if(count==5'b00000)
        empty<=1;
    else
        empty<=0;
end
endmodule


module sync_fifo(input clk,
                    input rst,
                    output [7:0]data_out,
                    output full,
                    output empty,
                    output reg  [4:0]time_cnt);

wire wr_en;
wire rd_en;
wire [3:0]rd_addr;
wire [3:0]wr_addr;
reg [7:0]data_in;


ram ram_top(.clk(clk),
                .rst(rst),
                .wr_en(wr_en),
                .rd_en(rd_en),
                .wr_addr(wr_addr),
                .rd_addr(rd_addr),
                .data_in(data_in),
                .full(full),
                .empty(empty),
                .data_out(data_out));
                
rd_addr_gen rd_addr_gen_top(.clk(clk),
                                    .rst(rst),
                                    .rd_en(rd_en),
                                    .empty(empty),
                                    .rd_addr(rd_addr));
                                    
wr_addr_gen wr_addr_gen_top(.clk(clk),
                                    .rst(rst),
                                    .wr_en(wr_en),
                                    .full(full),
                                    .wr_addr(wr_addr));
                                    
flag_gen flag_gen_top(.clk(clk),
                            .rst(rst),
                            .wr_en(wr_en),
                            .rd_en(rd_en),
                            .full(full),
                            .empty(empty));
                            
                            
always @(posedge clk or negedge rst)
begin
   if (rst == 1'b0)
      data_in <= 8'd0;
   else if (data_in == 8'd15)
      data_in <= 8'd0;
    else if (data_in >= 8'd0 && data_in < 8'd15)
      data_in <= data_in + 8'd1;
end

always @(posedge clk or negedge rst)
begin 
    if (!rst)
          time_cnt <=5'b0;
            else if (data_in == 5'd31)
            time_cnt <=5'b0;
            else if (data_in >= 5'd0 && data_in < 5'd31)
            time_cnt <= time_cnt + 5'd1;
    end

assign wr_en = (time_cnt >= 1'b0 && time_cnt <= 5'd15) ? 1'b1:1'b0;
assign rd_en = (time_cnt >= 5'd16 && time_cnt <= 5'd31) ? 1'b1:1'b0;

endmodule

五、Testbench代码

`timescale 1 ns/ 1 ps
module sync_fifo_vlg_tst();
reg eachvec;
reg clk;
reg rst;
reg rd_en;
reg wr_en;
reg [7:0] data_in;
wire [7:0]  data_out;
wire empty;
wire full;
reg [7:0]dindely;

sync_fifo tb1 (.clk(clk),
                    .data_in(data_in),
                    .data_out(data_out),
                    .empty(empty),
                    .full(full),
                    .rd_en(rd_en),
                    .rst(rst),
                    .wr_en(wr_en));

always @(posedge clk)
begin
    dindely<=data_in;
end

initial                                                
begin                                                  
    clk=0;
    rst=0;
    rd_en=0;
    wr_en=0;
    data_in=0;
    #40
    rst=1;
    #35
    wr_en=1;
    #400
    rd_en=1;                                                       
end                                                    

always #20 data_in<=data_in+1;
always #10 clk=~clk;

endmodule

六、最终项目实现状况

实际设计时间:5天
实际占用逻辑单元数量:1026

项目实现截图及分析:
fifo6
fifo7
 
  分析:时间单位为1ns,时间精度为1ps,每10ns时钟信号反转一次。首先将各信号置0初始化,在40ns后将复位信号置为1,经过35ns后将写使能信号置为1,使FIFO开始写入数据,经过20ns后将读信号置为1,此时读写并存,写入数据由一for循环进行20次递增赋值,在写使能信号有效时将数据存入RAM,然后在读使能信号有效时将数据读出,然后按顺序显示。仿真结果表示顺序输入顺序输出,输出顺序与输入顺序相同,则同步FIFO功能正常。

七、遇到的问题及解决办法

Q:问题;S:解决方法。
  1. Q: 开发板连接PC的USB接口时驱动安装异常。
    S: 下载时要保持开发板电源开启,并安装合适的库。
  2. Q: 下载程序时端口设置异常。
    S: 端口设置可以利用其他空闲端口进行协调。
  3. Q: Modelsim仿真时没有波形。
    S: Modelsim仿真注意仿真对象模块要是激励块,不能为设计块。
  4. Q: 在QuartusII中仿真时always中的判断条件种类太多时会出错。
    S: QuartusII中always判断条件只能为电平触发或边沿触发中的一种类型,如想要多种条件可以在always块中用if来判断。
  5. Q: 在QuartusII中调用Modelsim进行仿真时出错,无法打开Modelsim软件。
    S: 在QuartusII中的Modelsim路径设置中,如果Modelsim软件为OEM版本时要在Modelsim-Altera中设置软件路径,其他版本则在Modelsim中设置。
  6. Q: 使用QuartusII打开MOdelsim仿真无波形。
    S: 再QuartusII中设置的testbench中路径或模块名要对应。

八、附录

1.RTL视图

fifo8
fifo9
fifo10
fifo11

2.SignalTapII仿真

fifo12

评论已关闭

Loading...
Fullscreen Image