文档章节

浅谈XILINX FPGA Block RAM 使用

msxbo
 msxbo
发布于 09/23 14:38
字数 2499
阅读 65
收藏 0

1.1概述

对于BRAM 详细的说明在XILINX 官方文档,pg058中有说明,我们这里仅对课程涉及的内容讲解。

Xlinx系列FPGA,包含两种RAM:Block RAM和分布式RAM(Distributed RAM),他们的区别在于,Block RAM是内嵌专用的RAM,而Distributed RAM需要消耗珍贵的逻辑资源组成。前者具有更高的时序性能,而后者由于分布在不通的位置,延迟较大。

1.2 BRAM RAM的应用形式

1.2.1单口ROM (Single-Port ROM)

单口ROM,就是数据只读的,需要在IP初始化的时候,对ROM进行初始化,而且只有一个读接口。

1.2.2双口ROM(Dual-port ROM)

端口A和端口B可以同时访问ROM

1.2.3单口RAM(Single-port RAM)

单口RAM只能一个时刻写,一个时刻读

1.2.4简单双口RAM(Simple Dual-port RAM)

A端口写,B端口读

1.2.5真双口RAM(True Dual-port RAM)

A 端口和B端口都可以读或者写

 

1.3 BLOCK RAM的读写模式

支持3种模式,分别是Write First Mode, Read First Mode, No Change Mode

1.3.1先写模式(Write First Mode)

这种模式下:

1)写操作:设置WEA为1写入当前地址的数据,而且在下一个时钟DOUTA会输出当前写入的

2)读操作:设置WEA为0读出当前地址的数据,在下一个时钟,DOUTA有效

1.3.2先读模式(Read First Mode)

这种模式下:

1)写操作:设置WEA为1写入当前地址的数据,而且在下一个时钟DOUTA会输出之前这个地址的数据

2)读操作:设置WEA为0读出当前地址的数据,在下一个时钟,DOUTA有效

1.3.3不变模式(No Change Mode)

这种模式下:

1)写操作:设置WEA为1写入当前地址的数据,和前面两种方式不一样,DOUT保存不变

2)读操作:设置WEA为0读出当前地址的数据,在下一个时钟,DOUTA有效

 

1.4 支持字节写入(BYTE Write)

另外,BRAM还具备BYTE Write功能,这样可以只对某一个字节进行修改,从下图时序图可以看出,只要控制WEA就可以控制对具体哪一个BYTE进行写控制。

1.5访问冲突 (Collision Behavior)

BRAM 很好用,但是需要注意冲突的问题,就是对于同一个地址写或者读的时候需要注意。

1.5.1异步时钟处理原则

使用异步时钟,当一个端口将数据写入存储位置时,另一端口在指定的时间内不得读取或写入该位置。 器件数据手册中定义了该时钟到时钟的建立时间,以及其他Block RAM切换特性。这里说到的” 时钟到时钟的建立时间”我还没注意到哪一个文档有说明。所以异步时钟可以通过长期的稳定性测试获取到这个时间间隔。

1.5.2同步时钟处理原则

同步写冲突:如果两个端口都试图写到内存中的同一位置,则会发生写写冲突。 内存位置的结果内容未知。 请注意,Write-Write冲突会影响内存内容,而Write-Read冲突只会影响数据输出

使用字节写入:使用字节写入时,在同一数据字中写入单独的字节时,存储器内容不会损坏。 仅当两个端口都试图写入同一字节时,RAM内容才会损坏。 下图说明了这种情况。 假设addra = addrb = 0

同步读写冲突:如果一个端口尝试写入内存位置而另一个端口读取相同的位置,则可能发生同步读写冲突。 虽然在写-读冲突中存储器的内容没有损坏,但是输出数据的有效性取决于写端口的工作模式。

a: 如果写入端口处于READ_FIRST模式,则另一个端口可以可靠地读取旧的存储器内容。

b: 如果写入端口处于WRITE_FIRST或NO_CHANGE模式,则读取端口的输出上的数据无效。

c: 如果是字节写入,则只有更新的字节在读取端口输出上无效,但是RAM中的内容是对的

 

下图说明了读写冲突和字节写入的影响。 当端口A处于WRITE_FIRST模式和READ_FIRST模式时,显示doutb。 假定addra = addrb = 0,端口B始终处于读取状态,并且所有内存位置均初始化为0。RAM的内容在读写冲突中不会被破坏。

1.5.3简单的双端口RAM冲突

对于简单双端口RAM,无论时钟如何,都可以使用READ_FIRST,WRITE_FIRST和NO_CHANGE工作模式。

简单双端口RAM就像真正的双端口RAM,其中仅连接了A端口的Write接口和B端口的Read接口。 工作模式定义了A或B端口的读写关系,并且仅在地址冲突期间影响A和B端口之间的关系。

对于同步时钟和冲突期间,可以配置端口A的写模式,以便对端口B的读操作可以产生数据(作用类似于READ_FIRST),也可以产生未定义的数据(Xs)。 因此,始终建议在配置为简单双端口RAM时使用READ_FIRST。 对于异步时钟,Xilinx建议将端口A的写入模式设置为WRITE_FIRST以确保碰撞安全。 有关此行为的详细信息,请参阅pg058第51页的冲突行为。

对于7系列设备,当RAM_MODE设置为ture dual port时,选定的操作模式将传递到Block RAM。 对于将RAM_MODE设置为simple dual port的原语,写模式为READ_FIRST用于同步时钟,而WRITE_FIRST用于异步时钟。

对于基于UltraScale架构的设备,没有限制,并且无论时钟如何,所选的操作模式总是传递给Block RAM原语。这一段说明,这种高级模式我们暂时不涉及。

 

其他内存冲突限制:地址空间重叠
7系列FPGA Block RAM存储器在以下配置中具有附加的冲突限制:
•当配置为真双端口(ture dual port)
•当CLKA(端口A)和CLKB(端口B)异步时
•在同时执行读写操作的应用程序中
•使用配置为READ_FIRST的写入模式配置端口A,端口B或两个端口时

 

上面文字描述中很多都在讲解冲突,其实对于我们的具体应用而言,更多时候我们BRAM是做乒乓使用的,也就是读地址和写地址,都是不会同时发生,而且时钟是同步的,这样就不容易发生冲突导致的数据破坏,和不正确。

 

1.6输出寄存器

BRAM 可以设置有寄存器输出和无寄存器输出,下图是BRAM的框图结构

下图是有寄存器和无寄存器输出,可以达到的最高时钟频率的数据表,所以增加寄存器输出可以提高速度。我们例子中由于用到的演示时钟并没有很高所以不需要增加寄存器输出。

1.6.1读取数据和读取使能延迟,无输出寄存器

1.6.2通过原始输出寄存器读取数据并实现rEad使能延迟

1.6.3使用两个流水线阶段读取数据和读取启用延迟

1.7添加BRAM IP

设置简单双口RAM

设置BRAM的端口A的宽度和深度

设置BRAM的端口B的宽度和深度,并且没有寄存器输出

 

这一页默认

单击OK

1.8读写BRAM代码

本代码的设计和FIFO使用非常类似

1)、写操作:写操作不断进行,每次写入1024个数据

2)、读操作:读操作是在每次写入达到512个数据开始的,当然实际上读操作完全可以和写操作同时进行,错开512个数据是为了方便观察现象。

module Bram_test(

input rstn_i,

input sys_clk_i

);

 

 

reg [9:0]addra;

reg [7:0]wr_frame;

reg WR_S;

reg ena;

reg wea;

 

always @(posedge sys_clk_i)begin

    if(!rstn_i)begin

       wr_frame <= 8'd0;

       addra    <= 9'd0;

       ena  <= 1'b1;

       wea  <= 1'b0; 

       WR_S <= 1'd0;

    end

    else begin

        case(WR_S)

        0:begin

             addra  <= 10'd0;

             ena    <= 1'd1;

             wea    <= 1'b1;

             WR_S   <= 1'b1;

        end

        1:begin

            if(addra != 10'd1023)begin //写数据的1024个地址

               wea   <= 1'b1;

               ena   <= 1'b1;

               addra <= addra + 1'b1;

            end

            else begin

               wea   <= 1'b0;

               ena   <= 1'b0;

               wr_frame <= wr_frame +1'b1;//帧计数器

               WR_S   <= 1'b0;

            end

        end

        endcase

     end

end

 

 

reg [9:0]addrb;

reg RD_S;

reg enb;

 

always @(posedge sys_clk_i)begin

    if(!rstn_i)begin

       addrb <= 9'd0;

       enb   <= 1'b0;

       RD_S  <= 1'd0;

    end

    else begin

        case(RD_S)

        0:begin

            enb     <= 1'b0;

            addrb   <= 10'd0;

             if(addra == 10'd512)begin//读数据在写数据的第512个地址开始

                enb   <= 1'b1;

                RD_S  <= 1'b1;

             end   

        end

        1:begin

            if(addrb != 10'd1023)begin//读出1024个数据

               enb    <= 1'b1;

               addrb  <= addrb + 1'b1;

            end

            else begin

               enb    <= 1'b0;

               addrb  <= 10'd0;

               RD_S   <= 1'b0;

            end

        end

        endcase

     end

end   

 

wire [31:0] dina = {wr_frame,wr_frame,addra[7:0],addra[7:0]};  

wire [31:0] doutb;

blk_mem_gen_0 bram_inst (

      .clka(sys_clk_i),    // input wire clka

      .ena(ena),      // input wire ena

      .wea(wea),      // input wire [0 : 0] wea

      .addra(addra),  // input wire [9 : 0] addra

      .dina(dina),    // input wire [31 : 0] dina

      .clkb(sys_clk_i),    // input wire clkb

      .enb(enb),      // input wire enb

      .addrb(addrb),  // input wire [9 : 0] addrb

      .doutb(doutb)  // output wire [31 : 0] doutb

    ); 

1.9 仿真文件

module tb_bram_test;

reg sys_clk_i;

reg rstn_i;

 

Bram_test Bram_test_inst(

.sys_clk_i(sys_clk_i),

.rstn_i(rstn_i)

);

   

   initial begin

      sys_clk_i = 1'b0;

      rstn_i    = 1'b0;

      #5;sys_clk_i = 1'b1;

      #5;sys_clk_i = 1'b0;

      #5;sys_clk_i = 1'b1;

      #5;sys_clk_i = 1'b0;

      #5;sys_clk_i = 1'b1;

      #5;sys_clk_i = 1'b0;

      #5;sys_clk_i = 1'b1;

      #5;sys_clk_i = 1'b0;

      #5;sys_clk_i = 1'b1;

                      

      rstn_i    = 1'b1;

      forever

         #5 sys_clk_i = ~sys_clk_i;

   end

                           

endmodule

1.10 仿真结果

箭头1 写开始

箭头2 写完1024个数据

箭头3 当写数据达到512个后开始读

箭头4 读完1024个数据

 

© 著作权归作者所有

msxbo
粉丝 6
博文 125
码字总数 319238
作品 0
南京
私信 提问
H.265/HEVC视频编码:FPGA GPU QSV实现对比

1. 背景 随着视频采集及传输技术的发展,视频素材的分辨率和帧率在不断提升。分辨率从2K到4K到8K;帧率从30到60到120;新的标准及技术,比如HDR,也不断出现。 素材质量的增长,图像码流量也...

大锤强
2018/04/27
0
0
H.265/HEVC视频编码: FPGA GPU QSV实现对比

1. 背景 随着视频采集及传输技术的发展,视频素材的分辨率和帧率在不断提升。分辨率从2K到4K到8K;帧率从30到60到120;新的标准及技术,比如HDR,也不断出现。 素材质量的增长,图像码流量也...

大锤强
2018/04/27
0
0
单口RAM、伪双口RAM、双口RAM与FIFO的区别

FPGA设计中,常用到的数据缓存IP有FIFO和RAM,其中RAM又分单口RAM、伪双口RAM、双口RAM。 单口与双口的区别在于,单口只有一组数据线与地址线,因此读写不能同时进行。而双口有两组数据线与地...

j_m
2012/09/17
192
0
出题率最高的30道FPGA面试题及其答案

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。 https://blog.csdn.net/mochenbaobei/article/details/82730726 1.什么是同步逻辑和异步逻辑...

FPGA技术联盟
07/08
0
0
网络硬件设计工具--NetFPGA

NetFPGA 是一款低功耗的开发平台,作为网络硬件教学和路由设计的设计工具。NetFPGA可以很方便的使得研究人员或者高校的学生搭建一个高速、硬件加速的网络系统。兴起于北美,最初只是斯坦福大...

叶秀兰
2015/03/12
1K
0

没有更多内容

加载失败,请刷新页面

加载更多

java通过ServerSocket与Socket实现通信

首先说一下ServerSocket与Socket. 1.ServerSocket ServerSocket是用来监听客户端Socket连接的类,如果没有连接会一直处于等待状态. ServetSocket有三个构造方法: (1) ServerSocket(int port);...

Blueeeeeee
20分钟前
2
0
用 Sphinx 搭建博客时,如何自定义插件?

之前有不少同学看过我的个人博客(http://python-online.cn),也根据我写的教程完成了自己个人站点的搭建。 点此:使用 Python 30分钟 教你快速搭建一个博客 为防有的同学不清楚 Sphinx ,这...

王炳明
昨天
4
0
黑客之道-40本书籍助你快速入门黑客技术免费下载

场景 黑客是一个中文词语,皆源自英文hacker,随着灰鸽子的出现,灰鸽子成为了很多假借黑客名义控制他人电脑的黑客技术,于是出现了“骇客”与"黑客"分家。2012年电影频道节目中心出品的电影...

badaoliumang
昨天
13
0
很遗憾,没有一篇文章能讲清楚线程的生命周期!

(手机横屏看源码更方便) 注:java源码分析部分如无特殊说明均基于 java8 版本。 简介 大家都知道线程是有生命周期,但是彤哥可以认真负责地告诉你网上几乎没有一篇文章讲得是完全正确的。 ...

彤哥读源码
昨天
13
0
jquery--DOM操作基础

本文转载于:专业的前端网站➭jquery--DOM操作基础 元素的访问 元素属性操作 获取:attr(name);$("#my").attr("src"); 设置:attr(name,value);$("#myImg").attr("src","images/1.jpg"); ......

前端老手
昨天
6
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部