根据网上资料和自己理解整合而成,参考文章和代码链接在文章结尾。

FIFO和RAM

1、 FIFO
FIFO是英文First In First Out 的缩写,是一种先进先出的数据缓存器,他与普通存储器的区别是没有外部读写地址线,这样使用起来非常简单,但缺点就是只能顺序写入数据,顺序的读出数据,其数据地址由内部读写指针自动加1完成,不能像普通存储器那样可以由地址线决定读取或写入某个指定的地址。
FIFO可以用于:(1)跨时钟域多bit传输:读写可以由不同的时钟控制,使用异步FIFO可以在两个不同时钟系统之间快速方便的传输数据。(2)数据匹配:对于不同宽度的数据接口可以使用FIFO,比如写入数据宽度为8bit,读取数据宽度为16bit,通过FIFO数据缓存器就可以达到数据匹配。(3)低延迟内存缓冲:当需要处理串行输入数据时,可以利用FIFO缓存原始数据,直到数据处理完毕。
一、 类型
根据FIFO工作的时钟域,可以将FIFO分为同步FIFO和异步FIFO。同步FIFO是指读时钟和写时钟为同一个时钟。在时钟沿来临时同时发生读写操作。异步FIFO是指读写时钟不一致,读写时钟是互相独立的。
Xilinx的FIFO IP配置如下:

根据存储器的不同,有四种同步FIFO(Common clock),三种异步FIFO(independent clocks)。block RAM是FPGA中定制的ram资源,而distribute RAM则是由LUT构成的RAM资源。由此区别表明,当FIFO较大时应选择block RAM,当FIFO较小时,选择distribute RAM.另外一个很重要的就是block RAM支持读写不同宽度,而distribute不支持。在这里为了更全面的了解FIFO,选择block RAM以拥有非对称方向速率的特性,内嵌FIFO(Builtin FIFO)在5以上的FPGA芯片中才存在。
其中,不同的存储器的FIFO对应的配置也有些许不同。例如:只有Block RAM资源的FIFO可以变换位宽,其他三种都不可以;异步FIFO中,只有Block RAM的Enable Safety Circuit配置可选,其他两种都不可选。
二、 基本代码操作写法
最基本的要求:在状态“满”时,不能进行写操作;在状态“空”时,不能进行读操作。
1.同步FIFO
同步FIFO一般用作数据缓存和位宽变换,根据缓存的数据量和位宽变换情况改变FIFO的深度和位宽。其中高位宽转换为低位宽,高位先输出;低位宽转高位宽,先进的在高位。

1) module fifo_w18d1024
2) (
3) input clk,
4) input rst_n,
5) input [18-1:0]din,
6) input din_vld,
7) input dout_vld,
8)
9) output fifo_valid,
10)    output [18-1:0]dout
11)    );
12)    //-----------------------------------------------
13)
14)    wire fifo_rd_en;
15)    wire fifo_wr_en;
16)    wire fifo_empty;
17)    wire fifo_full;
18)    //-----------------------------------------------
19)
20)    fifo_w18d1024 u_fifo_w18d1024 (
21)      .clk(clk),            // input wire clk
22)      .srst(~rst_n),            // input wire srst
23)
24)      .din(din),            // input wire [17 : 0] din
25)      .wr_en(fifo_wr_en),   // input wire wr_en
26)
27)      .rd_en(fifo_rd_en),   // input wire rd_en
28)      .dout(dout),               // output wire [17 : 0] dout
29)
30)      .full(fifo_full),     // output wire full
31)      .empty(fifo_empty),   // output wire empty
32)
33)      .valid(fifo_valid)   // output wire valid
34)    );
35)
36)    assign fifo_wr_en = din_vld  & (~fifo_full);
37)    assign fifo_rd_en = dout_vld & (~fifo_empty);
38)
39)    endmodule

2.异步FIFO
根据写时钟和读时钟的关系确定FIFO的深度,不能出现溢出的情况。fifo的复位需要一段时间,期间wr_rst_busy和rd_rst_busy信号为高电平,此时应禁止读写FIFO,否则会造成数据丢失。

    module fifo_w18d32
2     (
3     input clk_120m,
4     input clk_240m,
5     input rst_n,
6     input [18-1:0]din,
7     input din_vld,
8     input dout_vld,
9
10    output fifo_valid,
11    output [18-1:0]dout
12    );
13    //-----------------------------------------------
14
15    wire fifo_rd_en;
16    wire fifo_wr_en;
17    wire fifo_empty;
18    wire fifo_full;
19    //-------------------------------------------
20
21    fifo_d128w32 u_fifo_d128w18 (
22      .rst(~rst),                  // input wire rst
23
24      .wr_clk(clk_240m),            // input wire wr_clk
25      .rd_clk(clk_120m),            // input wire rd_clk
26
27      .din(din),                  // input wire [31 : 0] din
28      .wr_en(fifo_wr_en),              // input wire wr_en
29
30      .rd_en(fifo_rd_en),              // input wire rd_en
31      .dout(dout),                // output wire [31 : 0] dout
32
33      .full(fifo_full),                // output wire full
34      .empty(fifo_empty),              // output wire empty
35
36      .valid(fifo_valid),              // output wire valid
37
38      .wr_rst_busy(wr_rst_busy),  // output wire wr_rst_busy
39      .rd_rst_busy(rd_rst_busy)  // output wire rd_rst_busy
40    );
41
42    assign fifo_wr_en = din_vld  & (~fifo_full) &(~wr_rst_busy);
43    assign fifo_rd_en = dout_vld & (~fifo_empty) &(~rd_rst_busy);
44
45    endmodule

2、 Ram
RAM 即随机存取存储器,与先入先出的FIFO不同,RAM读出的数据顺序跟写入数据顺序不一致,可以向RAM中的任意位置写入数据,也可以读取任意的位置的数据。
FIFO没有写地址和读地址,只能按顺序读写数据,而RAM具有读写地址,因此可以读写任意地址。

FIFO常用于数据传输通道中,用于缓存数据或时钟域变换;RAM则常用于存储指令或者中间的数据。
一、 类型
Xilinx的block memory generator ip中的block ram有三种:单口RAM、简化双口RAM和真双口RAM。单口与双口的区别在于,单口只有一组数据线与地址线,因此读写不能同时进行。而双口有两组数据线与地址线,读写可同时进行。

当ena=1时,douta正常输出;
当ena=1 &wea=1时,dina正常输入;
双口RAM分伪双口RAM(Xilinx称为Simple two-dual RAM)与双口RAM(Xilinx称为true two-dual RAM)。
伪双口RAM,一个端口只读,另一个端口只写,且写入和读取的时钟可以不同,位宽比可以不是1:1;

而双口RAM两个端口都分别带有读写端口,可以在没有干扰的情况下进行读写,彼此互不干扰。

FIFO也是一个端口只读,另一个端口只写。FIFO与伪双口RAM的区别在于,FIFO为先入先出,没有地址线,不能对存储单元寻址;而伪双口RAM两个端口都有地址线,可以对存储单元寻址;

异步时钟域的缓存只要是双口器件都可以完成,但FIFO不需对地址进行控制,是最方便的。
二、 基本代码操作写法
根据Xilinx官方文档,不同的RAM主要应用如下:
• 单口 RAM:处理器暂存数据、查找表
• 简单双口 RAM:内容可寻址存储器、FIFO
• 真双口 RAM:多处理器存储
1、 单口RAM

    //单口RAM用作数据缓存
2     spram_blk_w16d16 spram_blk_w16d16 (
3       .clka(clk),    // input wire clka
4       .wea(1'b1),      // input wire [0 : 0] wea
5
6       .addra(ram_addr),  // input wire [3 : 0] addra
7       .dina(din),    // input wire [15 : 0] dina
8       .douta(dout)  // output wire [15 : 0] douta
9     );
10    always @(posedge clk or negedge rst_n)
11    begin
12      if(rst_n == 1’b0)
13       ram_addr <= 1’d0;
14      else if(ram_addr>=DLY_NUM)
15       ram_addr <= 1’d0;
16      else
17       ram_addr <= ram_addr + ’d1;
18    end

2、 双口RAM
双口RAM经常用于跨时钟域处理,且比FIFO灵活性更大。输入数据速率20MHz,输出数据速率100Mhz,使用双口RAM完成跨时钟域处理。一次传输的数据为1024个,假设数据位宽为8bit,使用两片宽度为8、深度为1024的双口RAM完成数据传输。
使用乒乓操作提高读写效率,写RAM1时,读取RAM2中的数据;写RAM2时,读取RAM1中的数据。数据读取速率为数据写入速率的5倍,因此写数据端可以一直保持数据写入,而读数据端按写入一组数据时间的1/5进行,使用out_valid信号表示读出的数据有效。
Vivado环境下,RAM使用Block Memory Generator IP核配置,存储类型选择为“True Dual Port RAM”。RAM在读取数据时根据配置参数会有一定的延迟,设计时要注意时序对齐(否则会容易丢掉开头或结尾的一些数据),代码如下:
1. 此处代码出处见结尾参考链接。

`timescale 1ns / 1ps
2.
3.    module DualRAM
4.    (
5.        input clk_wr,      //写时钟速率20Mhz
6.        input clk_rd,      //读时钟速率100Mhz
7.        input rst_n,
8.        input [7:0] din,
9.        output reg out_valid,
10.       output reg [7:0] dout
11.   );
12.
13.   reg [9:0] addr_wr, addr_rd;
14.   reg en_wr1, en_wr2, we_wr1, we_wr2, en_rd1, en_rd2;
15.   wire [7:0] dout1, dout2;
16.
17.   dual_port_ram u1 (
18.     .clka(clk_wr),      //写端口
19.     .ena(en_wr1),
20.     .wea(we_wr1),
21.     .addra(addr_wr),
22.     .dina(din),
23.     .douta(),
24.     .clkb(clk_rd),      //读端口
25.     .enb(en_rd1),
26.     .web(1'b0),
27.     .addrb(addr_rd),
28.     .dinb(8'd0),
29.     .doutb(dout1)
30.   );
31.
32.   dual_port_ram u2 (
33.     .clka(clk_wr),      //写端口
34.     .ena(en_wr2),
35.     .wea(we_wr2),
36.     .addra(addr_wr),
37.     .dina(din),
38.     .douta(),
39.     .clkb(clk_rd),      //读端口
40.     .enb(en_rd2),
41.     .web(1'b0),
42.     .addrb(addr_rd),
43.     .dinb(8'd0),
44.     .doutb(dout2)
45.   );
46.
47.   //写端口乒乓操作
48.   always @ (posedge clk_wr)     //写地址信号控制0~1023
49.       if (!rst_n) addr_wr <= 1023;
50.       else addr_wr <= addr_wr + 1'b1;
51.
52.   always @ (posedge clk_wr)     //轮流写RAM1与RAM2
53.       if (!rst_n) begin we_wr1 <= 1'b1; we_wr2 <= 1'b0;
54.           en_wr1 <= 1'b1; en_wr2 <= 1'b0;  end
55.       else if (addr_wr == 1023) begin
56.           we_wr1 <= ~we_wr1; we_wr2 <= ~we_wr2;
57.           en_wr1 <= ~en_wr1; en_wr2 <= ~en_wr2;
58.       end
59.
60.   //读端口乒乓操作
61.   always @ (posedge clk_rd)    //读地址信号控制0~1023
62.       if (!rst_n) addr_rd <= 1021;  //匹配延迟
63.       else addr_rd <= addr_rd + 1'b1;
64.
65.   reg [15:0] cnt;
66.   always @ (posedge clk_rd)    //读时钟为写时钟的5倍
67.       if (!rst_n) cnt <= 16'hFFFE;  //匹配延迟
68.       else if (cnt == 5119) cnt <= 0;
69.       else cnt <= cnt + 1'b1;
70.
71.   reg flag1, flag2;
72.   always @ (posedge clk_rd)    //读RAM标志,RAM1或RAM2
73.       if (!rst_n) begin flag1 <= 1'b1; flag2 <= 1'b0; end
74.       else if (cnt == 5119) begin flag1 = ~flag1; flag2 = ~flag2; end
75.       else begin flag1 <= flag1; flag2 <= flag2; end
76.
77.   always @ (posedge clk_rd)    //读RAM使能,选择cnt的前1/5时间读取
78.       if (!rst_n) begin en_rd1 <= 1'b1; en_rd2 <= 1'b0; end
79.       else if (cnt < 1024) begin en_rd1 <= flag1; en_rd2 <= flag2; end
80.       else begin en_rd1 <= 1'b0; en_rd2 <= 1'b0; end
81.
82.   reg en_rd1_reg, en_rd2_reg;
83.   always @ (posedge clk_rd)    //延迟一级,匹配时序
84.       if (!rst_n) begin en_rd1_reg <= 0; en_rd1_reg <= en_rd1_reg; end
85.       else begin en_rd1_reg <= en_rd1; en_rd2_reg <= en_rd2; end
86.
87.   always @ (posedge clk_rd)    //输出选择,RAM1或RAM2;控制输出使能信号
88.       if (!rst_n) begin dout <= 0; out_valid <= 0; end
89.       else if (en_rd1_reg) begin dout <= dout1; out_valid <= 1; end
90.       else if (en_rd2_reg) begin dout <= dout2; out_valid <= 1; end
91.       else begin dout <= 0; out_valid <= 0; end
92.
93.   endmodule

NOTE1:
注意一下:RAM的操作模式(Operation mode)选项
每个端口的操作模式决定了此端口的读和写之间关系。端口 A 和 B 可以独立配置为以 下三种模式中任一模式:写优先模式,读优先模式,不改变模式。这些模式详解见下面。当 A 和 B 端口地址有冲突时,操作模式就会影响 A 和 B 口之间关系。
1.写优先模式(write first mode):
在写优先模式中,输入数据被自动写入存储器件中,并且出现在数据输出端口。时序见下图。这种传输模式增强了在同一端口写操作时使用数据输出总线的灵活性。(即输入数据的同时自动写进存储器和驱动数据到数据输出端)

2.读优先模式(read first mode):
在读优先模式中,预先存储在写地址中的数据会被输出,而输入数据被存入存储器件中。这种模式见下图。(即以前写进当前写地址的数据出现在数据输出端,此时输入的数据被保存到存储器中)

3.不改变模式(no-change mode):
在不改变模式中,输出锁存在写操作时候保持不变,见下图。在同一端口 的写操作不会对数据输出端口产生影响,输出仍然是以前的读数据。(即输出锁存器保持不改变在写操作期间。说明在写期间输出端不会输出写期间地址的数据,不管以前保存数据还是现在的输入数据)。

NOTE2:
Xilinx官方文档里利用寄存器构建单双口RAM的Verilog代码。

1、单口RAM


1.    // Single-Port Block RAM Write-First Mode (recommended template)
2.    // File: rams_02a.v
3.    //
4.    module v_rams_02a (clk, we, en, addr, di, do);
5.    input clk;
6.    input we;
7.    input en;
8.    input [9:0] addr;
9.    input [15:0] di;
10.   output [15:0] do;
11.   reg[15:0] RAM [1023:0];
12.   reg[15:0] do;
13.   always @(posedge clk)
14.   begin
15.    if (en)
16.    begin
17.    if (we)
18.    begin
19.    RAM[addr] <= di;
20.    do <= di;
21.    end
22.    else
23.    do <= RAM[addr];
24.    end
25.   end
26.   endmodule

2、 真双口RAM

    // Dual-Port Block RAM with Two Write Ports
2     // File: rams_16.v
3
4     module v_rams_16 (clka,clkb,ena,enb,wea,web,addra,addrb,dia,dib,doa,dob);
5
6     input clka,clkb,ena,enb,wea,web;
7     input [9:0] addra,addrb;
8     input [15:0] dia,dib;
9     output [15:0] doa,dob;
10    reg[15:0] ram [1023:0];
11    reg[15:0] doa,dob;
12
13    always @(posedge clka) begin if (ena)
14    begin
15     if (wea)
16     ram[addra] <= dia;
17     doa <= ram[addra];
18     end
19    end
20
21    always @(posedge clkb) begin if (enb)
22    begin
23     if (web)
24     ram[addrb] <= dib;
25     dob <= ram[addrb];
26     end
27    end
28
29    Endmodule

3、简单双口RAM

// Simple Dual-Port Block RAM with One Clock
2     // File: simple_dual_one_clock.v
3     module simple_dual_one_clock (clk,ena,enb,wea,addra,addrb,dia,dob);
4     input clk,ena,enb,wea;
5     input [9:0] addra,addrb;
6     input [15:0] dia;
7     output [15:0] dob;
8     reg[15:0] ram [1023:0];
9     reg[15:0] doa,dob;
10    always @(posedge clk) begin
11     if (ena) begin
12     if (wea)
13     ram[addra] <= dia;
14     end
15    end
16    always @(posedge clk) begin
17     if (enb)
18     dob <= ram[addrb];
19    end
20    endmodule

3、参考资料
https://www.cnblogs.com/xianyufpga/p/11073798.html
https://www.cnblogs.com/chengqi521/p/6831439.html
https://blog.csdn.net/zengaliang/article/details/78765159
https://blog.csdn.net/zengaliang/article/details/78765159
https://www.xilinx.com/support/documentation/sw_manuals/xilinx2014_4/ug901-vivado-synthesis.pdf
http://doc.embedfire.com/fpga/altera/ep4ce10_pro/zh/latest/code/pingpang.html
https://blog.csdn.net/FPGADesigner/article/details/83689811

Xilinx ip核之FIFO和RAM相关推荐

  1. Xilinx IP核之FIFO

    文章目录 背景 1.FIFO的介绍 2.IP核的配置 3.同步FIFO工程实例 4.异步FIFO 1.读写指针的概念 2.FIFO满空标志的产生 3.如何判断读写指针相等时候,为空还是为满呢? 4.异 ...

  2. 【正点原子FPGA连载】第十五章 IP核之FIFO实验 -摘自【正点原子】领航者ZYNQ之FPGA开发指南_V2.0

    1)实验平台:正点原子领航者ZYNQ开发板 2)平台购买地址:https://item.taobao.com/item.htm?&id=606160108761 3)全套实验源码+手册+视频下 ...

  3. FPGA学习笔记(十二)IP核之FIFO的学习总结

    系列文章目录 一.FPGA学习笔记(一)入门背景.软件及时钟约束 二.FPGA学习笔记(二)Verilog语法初步学习(语法篇1) 三.FPGA学习笔记(三) 流水灯入门FPGA设计流程 四.FPGA ...

  4. 异步fifo_正点原子开拓者FPGA开发板资料连载第十五章 IP核之FIFO实验

    1)实验平台:正点原子开拓者FPGA 开发板 2)摘自<开拓者FPGA开发指南>关注官方微信号公众号,获取更多资料:正点原子 3)全套实验源码+手册+视频下载地址:http://www.o ...

  5. IP核的使用之RAM(Vivado)

    IP核的使用之RAM(Vivado) 文章目录 IP核的使用之RAM(Vivado) 一.引言 二.RAM IP核及相关内容扫盲 1.RAM简介 2.RAM IP核分类(Xilinx) 三.分布式RO ...

  6. Xilinx IP核AXI Memory Mapped to PCI Express使用

    作者 QQ群:852283276 微信:arm80x86 微信公众号:青儿创客基地 B站:主页 https://space.bilibili.com/208826118 性能 测试平台:长城的FT15 ...

  7. 【以前】ModelSim中仿真Xilinx IP核

    先对题目进行说明:ModelSim本身是一个独立的仿真环境,不需要依赖其他的软件.这里所说的" ModelSim仿真XilinxIP核"是指单独运行ModelSim 进行仿真,而不 ...

  8. Xilinx IP解析之FIFO Generator v13.2

    一. IP概述 可参考Xilinx官网fifo_generator概述, 以下翻译自官网此IP的概述. 产品描述: LogiCORE™IP FIFO生成器内核生成经过充分验证的先进先出(FIFO)内存 ...

  9. xilinx IP核之ROM

    背景 xilinx的IP核中比较常用的还有ROM核,同CMT IP核一样,xilinx也提供了用于实现ROM功能的IP核较Block Memor Generator IP 核,这个IP核继承了单/双端 ...

最新文章

  1. 如何修改 远程桌面的 默认端口号 3389
  2. 对话微软黄学东:语音语言技术是镶在 AI 皇冠上的明珠
  3. 自定义对话框控件bate2----20050516
  4. windows下本地或者远程连接MYSQL数据库,报1130错误的解决方法
  5. Lua和C语言的交互——C API
  6. 、PHP只能访问mysql_php中 mysql函数不能调用,只有mysql_query()可以用
  7. CSS中常用的样式语法
  8. SVN学习总结(1)——SVN简介及入门使用
  9. cerely异步分布式
  10. html中post和get区别
  11. html输入地址提示错误,高德地图开发之输入框内伴随地址的输入,动态给出地址选择提示...
  12. 消息中间件学习总结(13)——Kafka与RocketMQ的单机系统可靠性比较分析
  13. CentOS Repos
  14. 系统在此应用程序中检测到基于堆栈的缓冲区_Linux 中的零拷贝技术
  15. web逻辑思维题目_逻辑思维训练500题
  16. 机器视觉运动控制一体机应用例程|U盘视觉定位激光打标解决方案
  17. 唯众高职人工智能技术应用专业解决方案
  18. 360p2刷无线打印服务器,360P2路由器怎么修改无线信道?-360安全路由P2的WiFi信道修改教程?...
  19. 说一说程序员如何保持平常心
  20. cesium获取当前屏幕中心点坐标

热门文章

  1. 计算出1到1000以内所有不能被7整除的整数之和
  2. php 域名恶意解析,防止恶意域名解析
  3. Delphi XE10.4 TrayIcon托盘
  4. 奋斗路上的安全边际,程序员保险配置指南
  5. [深度学习论文笔记]Multi-phase Liver Tumor Segmentation with Spatial Aggregation
  6. scrapy爬取海贼王漫画
  7. C语言函数调用栈(一)
  8. mysql 游标当前行,MySQL游标的使用笔记大全
  9. 全网最细华为路由器mpls 虚拟专用网络intranet 组网配置
  10. Excel和XML的相互转换(JAVA语言)