Quartus II 双口RAM例化详解
上节内容讲解了单口RAM及使用,本节着重介绍双口RAM及使用。由于双口RAM作为FPGA片上RAM应用极其广泛,使用相对灵活,是FPGA学习中应重点掌握的内容。目前双口RAM在CPU内存,程序存储器,数据缓存,数字信号处理,图像处理,通信等领域几乎是必不可少的部件,他与FIFO在数据缓存处理方面是一把双刃剑,学好双口RAM和FIFO是FPGA进阶学习的必经之路。
- 双口RAM的生成步骤
-
- 在Quartus II 新建工程, 工程名为dpram, 顶层实体为dpram,如图1, 器件选择Cyclone 10 LP 10CL006YE144C8G, 仿真选择Modelsim_Altera
图1
-
- 在新建的工程中添加双口RAM, 在IP Catalog区选择Install IP –>Libarary–>Basic Function–>On Chip Memory–>RAM :2-PORT, 双击,
- 在弹出的对话框中输入将生成的双口RAM变体的名称d_p_ram.
- 在图2中选择with two read /write ports,这是真正双口RAM的选择, 如果选择with one read port and one write port, 就是上节内容讲的单口RAM,双口RAM分为两个全功能端口,分别为a端口和b端口。a端口的接口为data_a, address_a,wren_a为输入端口,rden_a, q_a为输出端口。b端口同样也是data_b,address_b,wren_b为输入端口,rden_b,q_b为输出端口。
- RAM尺寸选择,按字指定。也可以按 bit指定RAM的数据端口宽度和深度。
图2
-
- 图3中选择数据宽度(根据,需要可以选择8位,9位,16位,18位,32位,36位等),如果选择16位,数据端口即为q_a[15..0],q_b[15..0],data_a[15..0],data_b[15..0]都是16位。也可以选择a端口与b端口的数据宽度不等,例如会经常使用的情况a端口是16位写入,b端口32位输出的情况,具体怎么使用主要根据需求设置。
- 根据工程的实际需求设置数据深度,这里数据深度选择512,即存储器阵列可以有512个16位单元阵列。
图3
-
- 图4中选择时钟的使用情况,可以使用单一时钟,也可以读、写分开使用不同时钟,但更多的是a,b端口使用不同的时钟。在a,b端口使用不同时钟时要注意在访问同一单元时亚稳态的情况,个与寄存器在不同时钟域的访问类似。后面会介绍在亚稳态的情况下,如何利用读写协议避免访问出错的情况。在a, b端口异步时钟的情况下,应避免使用数据拷贝的方式,也就是在读写时访问同一端口的情况下,避免使用new data选项。
- 添加rden_a, rden_b端口,可以在在写过程中当地址变化时通过设置rden_a,rden_b禁止q_a,q_b的端口数据变化,从而降低功耗。
- 如果数据连续读写,而且地址线与时钟同步,rden_a, rden_b端口可以设为常数1’b1;
- 对于rden_a, rden_b也可以在生成的时候不选择该项,此时默认为rden_a=1’b1; rden_b=1’b1; 但是如果选择了生成该项,如果在例化中忽略该项则默认rden_a=1’b0;den_b=1’b0; 因此在使用时一定要注意。
图4
-
- 图5设置输入、输出端口寄存方式,与单口RAM一致,这里不在详细解释。
图5
-
- 图6中创建时钟使能信号端口,在非操作双口RAM时,可以降低功耗。如data_a或data_b以及address的变化可能还有其它操作会使用(共享的端口),如果添加时钟使能,并在这些端口变化时保持enable_a,enable_b为低电平,则保持生成的IPCore 内的这些寄存器不翻转,从而降低功耗。本节的测试例子中没有添加时钟使能端的情况。
图6
-
- 图中选项是当访问的存储器的内容恰好出现读、写同一单元的情况,这里有4中可能的情况,a端口出现读写同一单元,b端口出现读写同一单元, a端口写于b端口读同一单元,b端口写与a端口读同一单元。当出现这4中组合时,在读端口选择读存储器的已有的旧数据还是当前正在更新的数据。原则如下:当读写是同一个时钟时(时钟同步)可以选择是旧数据(old data),也可以选择新数据(new data),特别是在作为CPU内存使用时,会出现流水线中上次写的数据,在本次希望程序执行时要用到该数据,但由于写数据有延迟,如果读旧数据将会得到过时的数据,引起程序执行错误,可以参见RISC-V CPU IPcore设计部分。
图7
-
- 图8 设计双口RAM中的初始化文件* .mif或.hex文件,这里的设置与ROM初始化相同,参照ROM的.mif文件的设置与使用。
图8
-
- 点击finish,并将产生的双端口RAM添加到工程中进行测试。
总结:在上面的步骤中生成了名为d_p_ram的双口RAM,两个端口分别为:
a端口(verilog格式): data_a[15:0], address_a[8:0], wren_a, rden_a, q_a[15:0], clock_a,
b端口(verilog格式): data_b[15:0], //b端口 输入数据 address_b[8:0], //b端口地址 wren_b, //b端口写使能 rden_b, //b端口读使能 q_b[15:0] , //b端口输出端口 clock_b, //b端口 时钟
- 双口RAM测试
- 新建verilog文件 dpram.v(模块名dpram,与工程的顶层实体名一致)
- 例化新生成的双口RAM,并将a,b端口重新整理,代码如下:
module dpram ( //==================================a side input inclka, input [8:0] address_a, input [15:0] data_a, input wren_a, input rden_a, output [15:0] q_a, //==================================b side input inclkb, input [8:0] address_b, input [15:0] data_b, input wren_b, input rden_b, output [15:0] q_b ); d_p_ram d_p_ram_inst ( .clock_a (inclka), .address_a (address_a), .data_a (data_a), //data --write to memory .rden_a (rden_a), .wren_a (wren_a), .q_a (q_a), //read, data out from memory .clock_b (inclkb), .address_b (address_b), .data_b (data_b), .rden_b (rden_b), .wren_b (wren_b), .q_b (q_b) ); endmodule