[Verilog HDL] VERILOG实现异步FIFO

[复制链接]
9840|1
 楼主| gaochy1126 发表于 2023-5-29 14:55 | 显示全部楼层 |阅读模式

首先要理解fifo的写满和读空,用多出来的一位来扩充地址

使用格雷码判断当,写指针追上读指针时候就是写满,为了方便判断当写满时候即,写地址地最高和次高位与读地址相反其他位相同时就是写满,
当读地址的所有位和写地址的所有位相等就是读空

代码根据图可以轻松得到:
下图是地址计数器的增加图


  1. 写控制器:
  2. module        w_ctrl(
  3.         input        wire                w_clk,//写时钟
  4.         input        wire                rst_n,//复位
  5.         input        wire                w_en,//写使能
  6.         input        wire        [8:0]        r_gaddr,//读时钟域过来的格雷码读地址指针
  7.         output        reg                w_full,//写满标志
  8.         output        wire        [8:0]        w_addr,//256深度的FIFO写二进制地址
  9.         output        wire        [8:0]        w_gaddr//写FIFO地址格雷码编码
  10. );
  11. reg        [8:0]        addr;
  12. reg        [8:0]        gaddr;
  13. wire        [8:0]        addr_wire;
  14. wire        [8:0]        gaddr_wire;
  15. reg        [8:0]        r_gaddr_d1,r_gaddr_d2;
  16. wire                w_full_wire;
  17. //打两拍进行时钟同步
  18. always @(posedge w_clk or negedge rst_n)
  19.         if(rst_n == 1'b0)
  20.                 {r_gaddr_d2,r_gaddr_d1} <= 18'd0;
  21.         else
  22.                 {r_gaddr_d2,r_gaddr_d1} <= {r_gaddr_d1,r_gaddr};
  23. //产生些ram的地址指针二进制
  24. assign w_addr = addr;
  25. always @(posedge w_clk or negedge rst_n)
  26.         if(rst_n == 1'b0)
  27.                 addr <= 9'd0;
  28.         else
  29.                 addr <= addr_wire;

  30. assign addr_wire = addr + ((~w_full)&w_en);
  31. //转换格雷码编码地址
  32. assign gaddr_wire=(addr_wire>>1)^addr_wire;
  33. always @(posedge w_clk or negedge rst_n)
  34.         if(rst_n == 1'b0)
  35.                 gaddr<=9'd0;
  36.         else gaddr <= gaddr_wire;
  37. assign w_gaddr = gaddr;

  38. //写满标志产生完成
  39. always @(posedge w_clk or negedge rst_n)
  40.         if(rst_n == 1'b0)
  41.                 w_full <= 1'b0;
  42.        
  43.         else if({~gaddr_wire[8:7],gaddr_wire[6:0]}==r_gaddr_d2)//根据仿真验证一下打两拍对空满标志的影响???
  44.                 w_full <=1'b1; //w_full_wire;
  45.         else w_full <=1'b0;


  46. endmodule



 楼主| gaochy1126 发表于 2023-5-29 14:55 | 显示全部楼层



  1. 读数据:

  2. module        r_ctrl (
  3.         input        wire                r_clk,//读时钟
  4.         input        wire                rst_n,
  5.         input        wire                r_en,//读使能
  6.         input        wire        [8:0]        w_gaddr,//写时钟域中的写地址指针
  7.         output        reg                r_empty,//读空标志
  8.         output        wire        [8:0]        r_addr,//读地址二进制
  9.         output        wire        [8:0]        r_gaddr//读格雷码地址
  10. );
  11. reg        [8:0]        addr;
  12. reg        [8:0]        gaddr;
  13. wire        [8:0]        addr_wire;
  14. wire        [8:0]        gaddr_wire;
  15. reg        [8:0]        w_gaddr_d1,w_gaddr_d2;
  16. wire                r_empty_wire;
  17. //打两拍进行时钟同步
  18. always @(posedge r_clk or negedge rst_n)
  19.         if(rst_n == 1'b0)
  20.                 {w_gaddr_d2,w_gaddr_d1} <= 18'd0;
  21.         else
  22.                 {w_gaddr_d2,w_gaddr_d1} <= {w_gaddr_d1,w_gaddr};
  23. //二进制的读地址
  24. assign r_addr = addr;
  25. always @(posedge r_clk  or negedge rst_n)
  26.         if(rst_n == 1'b0)
  27.                 addr <=9'd0;
  28.         else
  29.                 addr <= addr_wire;

  30. assign addr_wire = addr + ((~r_empty)&r_en);
  31. //格雷码的读地址
  32. assign r_gaddr = gaddr;
  33. assign        gaddr_wire = (addr_wire >>1 )^ addr_wire;

  34. always @(posedge r_clk or negedge rst_n)
  35.         if(rst_n == 1'b0)
  36.                 gaddr <= 9'd0;
  37.         else
  38.                 gaddr <= gaddr_wire;
  39.        
  40. //读空标志的产生
  41. assign r_empty_wire =(gaddr_wire == w_gaddr_d2);
  42. always @(posedge r_clk or negedge rst_n)
  43.         if(rst_n == 1'b0)
  44.                 r_empty<=1'b1;
  45.         else //if(gaddr_wire == w_gaddr_d2)//根据仿真验证一下打两拍对空满标志的影响???
  46.                 r_empty <= r_empty_wire;
  47.         //else
  48.                 //r_empty <= 1'b0;

  49. endmodule



  50. 下面是fifo_mem实现:
  51. module        fifomem(
  52.         input        wire                w_clk,
  53.         input        wire                r_clk,
  54.         input        wire                w_en,//来自于FIFO的写控制模块
  55.         input        wire                w_full,//来自于FIFO的写控制模块
  56.         input        wire        [7:0]        w_data,//来自于外部数据源
  57.         input        wire        [8:0]        w_addr,//来自于FIFO的写控制模块
  58.         input        wire                r_empty,//来自于FIFO的读控制模块
  59.         input        wire        [8:0]        r_addr,//来自于FIFO的读控制模块
  60.         input  wire          rst_n,
  61.         output        reg        [7:0]        r_data//读数据是从内部ram中读取
  62. );

  63. reg  [7:0]data_mem[255:0];
  64.         wire [7:0]w_addr_r;//定义写入地址
  65.         wire [7:0]r_addr_r;//定义写入地址
  66.         integer i;
  67.         assign w_addr_r = w_addr[7:0];
  68.         assign r_addr_r = r_addr[7:0];
  69.         //写入数据
  70.         always@(posedge w_clk or negedge rst_n)
  71.        
  72.         if(!rst_n)begin       
  73.        
  74.                 for(i = 0;i < 256;i = i + 1)begin
  75.                         data_mem[i] <= 8'b0;
  76.                 end
  77.         end
  78.                
  79.         else if(w_en &&(!w_full))//防止写溢出,如果已经满了禁止写数据
  80.                 data_mem[w_addr_r] <= w_data;
  81.         //读出数据
  82. always@(posedge r_clk or negedge rst_n)
  83.         if(!rst_n)
  84.                 r_data <= 0;
  85.         else
  86.                 r_data <=data_mem[r_addr_r];
  87. endmodule

  88. 下面是顶层模块:
  89. module        ex_fifo(
  90.         input        wire                w_clk,
  91.         input        wire                r_clk,
  92.         input        wire                rst_n,
  93.         input        wire                w_en,
  94.         input        wire        [7:0]        w_data,
  95.         output        wire                w_full,
  96.         input        wire                r_en,
  97.         output        wire        [7:0]        r_data,
  98.         output        wire                r_empty
  99. );
  100. wire        [8:0]        r_gaddr;
  101. wire        [8:0]        w_addr;
  102. wire        [8:0]        w_gaddr;
  103. wire        [8:0]        r_addr;
  104. w_ctrl        w_ctrl_inst(
  105.         .w_clk                (w_clk),//写时钟
  106.         .rst_n                (rst_n),//复位
  107.         .w_en                (w_en),//写使能
  108.         .r_gaddr        (r_gaddr),//读时钟域过来的格雷码读地址指针
  109.         .w_full                (w_full),//写满标志
  110.         .w_addr                (w_addr),//256深度的FIFO写二进制地址
  111.         .w_gaddr        (w_gaddr)//写FIFO地址格雷码编码
  112. );

  113. fifomem        fifomem_inst(
  114.         .w_clk                (w_clk),
  115.         .r_clk                (r_clk),
  116.         .w_en                (w_en),//来自于FIFO的写控制模块
  117.         .w_full                (w_full),//来自于FIFO的写控制模块
  118.         .w_data                (w_data),//来自于外部数据源
  119.         .w_addr                (w_addr),//来自于FIFO的写控制模块
  120.         .r_empty        (r_empty),//来自于FIFO的读控制模块
  121.         .r_addr                (r_addr),//来自于FIFO的读控制模块
  122.         .r_data                (r_data),//读数据是从内部ram中读取
  123.         .rst_n(rst_n)
  124. );

  125. r_ctrl r_ctrl_inst(
  126.         .r_clk                (r_clk),//读时钟
  127.         .rst_n                (rst_n),
  128.         .r_en                (r_en),//读使能
  129.         .w_gaddr        (w_gaddr),//写时钟域中的写地址指针
  130.         .r_empty        (r_empty),//读空标志
  131.         .r_addr                (r_addr),//读地址二进制
  132.         .r_gaddr        (r_gaddr)//读格雷码地址
  133. );

  134. endmodule

  135. 下面是testbench:

  136. `timescale        1ns/1ns

  137. module        tb_ex_fifo;
  138. reg                r_clk,w_clk,rst_n;
  139. reg                w_en;
  140. reg        [7:0]        w_data;
  141. reg                r_en;
  142. wire                w_full;
  143. wire                r_empty;
  144. wire        [7:0]        r_data;

  145. parameter        CLK_P=20;

  146. initial begin
  147.         rst_n<=0;
  148.         r_clk<=0;
  149.         w_clk<=0;
  150.        
  151.         #200
  152.         rst_n=1;
  153. end
  154. //写的初始化模块
  155. initial        begin
  156.         w_en=0;
  157.         w_data=0;
  158.         #300
  159.         write_data(256);
  160. end
  161. //读的初始化模块
  162. initial begin
  163.         r_en =0;
  164.         @(posedge w_full);
  165.         #40;
  166.         read_data(256);
  167. end

  168. always #(CLK_P/2) r_clk =~r_clk;
  169. always #(CLK_P/2) w_clk =~w_clk;

  170. ex_fifo ex_fifo_inst(
  171.         .w_clk                (w_clk),
  172.         .r_clk                (r_clk),
  173.         .rst_n                (rst_n),
  174.         .w_en                (w_en),
  175.         .w_data                (w_data),
  176.         .w_full                (w_full),
  177.         .r_en                (r_en),
  178.         .r_data                (r_data),
  179.         .r_empty        (r_empty)
  180. );


  181. task write_data(len);
  182.         integer i,len;
  183.         begin
  184.                 for (i=0;i<len;i=i+1)
  185.                 begin
  186.                         @(posedge w_clk);
  187.                         w_en=1'b1;
  188.                         w_data=i;
  189.                 end
  190.                 @(posedge w_clk);
  191.                 w_en = 1'b0;
  192.                 w_data =0;
  193.         end
  194. endtask

  195. task read_data(len);
  196.         integer i,len;
  197.         begin
  198.                 for (i=0;i<len;i=i+1)
  199.                 begin
  200.                         @(posedge r_clk);
  201.                         r_en=1'b1;
  202.                 end
  203.                 @(posedge r_clk);
  204.                 r_en = 1'b0;
  205.         end
  206. endtask
  207. endmodule


  208. 自己用modelsim仿真脚本如下:

  209. quit -sim
  210. .main clear

  211. vlib work

  212. vlog        ./tb_ex_fifo.v
  213. vlog        ./../design/*.v


  214. vsim        -voptargs=+acc        work.tb_ex_fifo

  215. add wave tb_ex_fifo/*
  216. add wave tb_ex_fifo/ex_fifo_inst/*
  217. add wave -divider {w}
  218. add wave tb_ex_fifo/ex_fifo_inst/w_ctrl_inst/*
  219. add wave -divider {R}
  220. add wave tb_ex_fifo/ex_fifo_inst/r_ctrl_inst/*

  221. run 25us


您需要登录后才可以回帖 登录 | 注册

本版积分规则

个人签名:这个社会混好的两种人:一是有权有势,二是没脸没皮的。

1205

主题

11937

帖子

26

粉丝
快速回复 在线客服 返回列表 返回顶部