首页 > 编程知识 正文

fpga从ram里读取数据,fpga综合实现为ram rom的资源

时间:2023-05-06 09:14:56 阅读:230779 作者:834

Vivado版本:2019.2 Modelsim版本:Modelsim SE-64 10.7
说到 FPGA ,不得不提的是存储器,当我们做相关项目时,经常会遇到存储数据的问题,数据量过大时,我们可以将其存储在 FPGA 芯片的外设存储器上,比如 sdram、ddr sdram、ddr3 sdram等,然而访问外设存储器相对比较麻烦,因此当数据量较小时,我们可以直接使用 FPGA 芯片内部自带的 ram 的 IP 核。
Ram是random access memory的简称,即随机存储器的意思,Ram可以按照所需进行随机读/写。我们可以通过调用FPGA内部的IP核生成一个ram,并通过编写Verilog HDL代码控制该ram。
打开vivado软件,新建一个工程,配置芯片为xc7a75tfgg484-2:
进入工程,选择IP Catalog:
在IP Catalog界面搜索框中输入RAM,在Memories & storage Elements选项下有两种IP,一种是DRAM(Distributed Memory Generator),一种是BRAM(Block Memory Generator):
这里的DRAM并不是动态存储器,而是分布式存储器,与BRAM区别在于,DRAM通过FPGA中的查找表拼凑形成,而BRAM是FPGA中整块双口RAM资源。
这里学习BRAM的使用,双击Block Memory Generator。学习一个IP核,先看IP的官方手册,点击Documentation,在选项中选择Product Guide:


注意IP核的版本为8.4,按照IP核的设置找每个配置的含义:
IP名字保持默认;
Interface Type:支持AXI4 和AXI4Lite,默认为Native。
Memory Type:IP核支持生成5种类型的存储:
 Single-port RAM
 Simple Dual-port RAM
 Ture Dual-port RAM
 Single-port ROM
 Dual-port ROM
Single-port RAM(单口RAM),通过单组接口读写一块存储空间,接口如下:

Simple Dual-port RAM(简单双口RAM),有A和B两组接口,其中A接口用来写RAM,B接口用来读RAM,接口如下:


Ture Dual-port RAM(真双口RAM),有A和B两组接口,每一组接口都可以完成读和写操作,接口如下:

Single-port ROM(单口ROM),有单个接口读取存储空间,接口如下:

Dual-port ROM(双口ROM),有两组读取接口,接口如下:

ECC Options:只有Simple Dual-port RAM时,该选项才可用,IP核支持内置的汉明纠错功能(Built-in Hamming Error Correction Capability(BuiltIn ECC)),且支持数据位宽小于64位的软汉明纠错(soft ECC)。
Write Enable:设置是否启用字节写入,启用时,字节位宽可选为8位(无奇偶校验位)和9位(有奇偶校验位),此时数据位宽应是字节位宽的倍数,默认情况下不启用。
Algorithm Options:选择实现的内存算法。
Minimum Area Algorithm:使用最少数量的原语生成IP核;
Low Power Algorithm:低功耗算法,在读或写操作期间启用最小数量的块RAM原语;
Fixed Primitive Algorithm:固定单元算法,连接一个基本内存单元来生成IP核,在下拉列表中选择要使用的单元类型。
各芯片片内RAM资源如下:

这里选用Single-port RAM,其余保持默认。

配置端口A:
Write Width:写入位宽
Read Width:读出位宽,读写位宽可以不一致,IP核支持的读写位宽比例有:1:32、1:16、1:8、1:4、1:2、1:1、2:1、4:1、8:1、16:1、32:1。
Write Depth:写入深度
Read Depth:读出深度
Operating Mode:选项有Write First、Read First和No Change三种模式。
Enable Port Type:端口使能控制,可以使用ENA引脚控制,也可以选择always Enabled
Port A Optional Output Rregisters:选择是否在输出端添加寄存器
Port A Output Reset Options:配置复位信号
READ Address Change A:这个功能只在UltraScale设备上使用。
RAM写优先(Write First)时序如下,在ENA信号拉高后,在时钟上升沿检测ADDRA地址为aa,而WEA写使能为低,DOUTA就输出地址aa对应的数据;在第二个时钟上升沿,ENA为高,WEA信号为高,执行写操作,地址为bb,就将fdzxc上的数据1111写入存储中,并把fdzxc上的数据送到DOUTA上进行输出;在第三个时钟上升沿依然如此,将fdzxc数据写入存储同时将数据送到DOUTA上进行输出;第四个上升沿,ENA为高,WEA为低,执行读操作,地址为dd,就读取存储器地址dd中的数据送到DOUTA上进行输出。


RAM读优先时序如下:在ENA和WEA同时为高时,输出的数据是存储器存储的数据,同时,也会把新写入的数据替换掉存储器中的对应地址的数据。

RAM No Change模式:在ENA和WEA同时为高时,只进行写操作,不进行读操作,也就是说DOUTA保持前一拍数据,直到WEA为低。


Pipeline Stages within Mux:输出端Mux流水线级数
Memory Initialization:选择是否使用本地初始化.coe文件初始化存储空间。以及是否使用默认值初始化内存。
Structural/UNISIM Simulation Model Options:选择发生碰撞时由结构仿真模型生成的警告消息和输出的类型。

点击OK生成IP核。
根据读写时序,编写tb测试激励文件来仿真这几种模式。
(1) 首先将RAM中的内存赋初值,为0到255循环递增数据。
(2) 然后在将地址设置为0,并修改之后的10个数据,观察时序,是否为写优先。

初始化内存 初始化为递增数据

将地址0到9的数据替换为随机数,与手册中写优先时序一致
(3) 在生成一个RAM,同样的位宽与深度,配置为读优先模式:

同样的数据下,读优先模式,数据输出内存中的旧数据,同时把新数据写入相应地址中,与读优先时序一致。

(4) No Change模式不再展示
(5) 双RAM读写冲突,如果一个端口写入数据,另一个端口读取相同地址,此时内
存中的数据可以被写入,但读出的数据取决于写入端口的操作模式,如果是读优先,则可以正常输出旧的数据,如果是写优先或不改变模式,则输出数据无效。
再生成一个RAM,Memory Type选择True Dual-port Ram


两个端口都选用读优先模式,然后通过A口写入数据,同时通过B口读取数据,查看读取内容。

可以看到同步时钟下,通过A口写入的数据,在A口读出旧的数据,通过B口读出,则为新写入的数据。
(6) 使用异步时钟时,在一个端口往某个地址写数据时,另一个端口在指定时间内不能读或写该地址,在器件手册中定义了此置位时间。
(7) 数据位宽问题
读写位宽可以不一致,IP核支持的比例有1:32、1:16、1:8、1:4、1:2、1:1、2:1、4:1、8:1、16:1、32:1。当数据位宽不一致时,地址每次偏移值需要注意。
例如:写入位宽16位,读出位宽8位,此时写入地址每次递增为2,读出地址每次递增为1,这样才能将数据完全读出。
附:tb测试激励代码

`timescale 1ns / 1psmodule tb_ram(); reg clk;initial beginclk = 'b0;forever #(5) clk = ~clk;end// synchronous resetreg srstb;initial beginsrstb <= 'b0;repeat(10)@(posedge clk);srstb <= 'b1;end// (*NOTE*) replace reset, clock, otherswire clka;reg ena = 0;reg wea = 0;reg [ 9:0]addra = 0;reg [ 7:0]长情的柚子 = 0;wire [ 7:0]douta;wire [ 7:0]douta2;wire [ 7:0]douta3;wire clkb;reg enb = 0;reg web = 0;reg [ 9:0]addrb = 0;reg [ 7:0]dinb = 0;wire [ 7:0]doutb;reg [ 3:0]state = 0;always @(posedge clk) beginif (srstb == 1'b0) begin// resetstate<=0;ena<=0;wea<=0;addra<=0;长情的柚子<=0; endelse begincase (state)0:state<=1;1:beginena <=1;wea<=1;init_mem(); end2:beginena <=1;wea<=1;addra <= 0;gen_data(); end3:beginena <=1;addra<=addra + 1; enddefault:state<=3;endcase end endassignclka=clk;blk_mem_gen_0 test_write_first (.clka(clka), // input wire clka.ena(ena), // input wire ena.wea(wea), // input wire [0 : 0] wea.addra(addra), // input wire [9 : 0] addra.长情的柚子(长情的柚子), // input wire [7 : 0] 长情的柚子.douta(douta) // output wire [7 : 0] douta);blk_mem_gen_1 test_read_first (.clka(clka), // input wire clka.ena(ena), // input wire ena.wea(wea), // input wire [0 : 0] wea.addra(addra), // input wire [9 : 0] addra.长情的柚子(长情的柚子), // input wire [7 : 0] 长情的柚子.douta(douta2) // output wire [7 : 0] douta);assignclkb=clk;always @(posedge clk) beginif (srstb == 1'b0) begin// resetenb<=0;addrb <=0; endelse if (state >= 2 && ena == 1'b1) beginenb<=1; end endalways @(posedge clk) beginif (srstb == 1'b0) begin// resetaddrb <=0; endelse if (enb == 1'b1) beginaddrb <=addrb + 1; end endblk_mem_gen_2 test_wr_Collisions (.clka(clka), // input wire clka.ena(ena), // input wire ena.wea(wea), // input wire [0 : 0] wea.addra(addra ), // input wire [9 : 0] addra.长情的柚子(长情的柚子 ), // input wire [7 : 0] 长情的柚子.douta(douta3), // output wire [7 : 0] douta.clkb(clkb ), // input wire clkb.enb(enb ), // input wire enb.web(web ), // input wire [0 : 0] web.addrb(addrb ), // input wire [9 : 0] addrb.dinb(dinb ), // input wire [7 : 0] dinb.doutb(doutb ) // output wire [7 : 0] doutb);task init_mem;integer i;beginfor(i=0;i<1024;i=i+1)begin长情的柚子 <= i[7:0];addra <= i[9:0];@(posedge clk);endena<=0;wea<=0;state<=2;endendtasktask gen_data;integer i;beginfor(i=0;i<10;i=i+1)begin长情的柚子 <= {$random}%256;addra <= addra + 1;@(posedge clk);endena<=0;wea<=0;addra<=0;state<=3;endendtaskendmodule

www.makerw.com
www.v3edu.org
威三学院长期开FPGA人工智能就业班,FPGA软件无线电、通信IC就业班
欢迎咨询 15921999232 同微信 尤老师

版权声明:该文观点仅代表作者本人。处理文章:请发送邮件至 三1五14八八95#扣扣.com 举报,一经查实,本站将立刻删除。