Menu Close

Quartus 单口RAM的生成与使用

上节内容讲解了ROM的生成、配置与使用,本节内容讲解利用FPGA内部的memory(可以是block memory 或是distribute memory),并举例说明单口RAM的使用。

1. 单口RAM的生成

  • 新建工程,工程名s_port_ram,顶层设计实体名s_port_ram, 器件选择 cyclone LP, 10CL006YE144C8G, EAD tools仿真(simulation)选择Modelsim-Altera. Format 选择Verilog
  • 在IP catalog选择单口RAM, IP Catalog–>Install IP–>Library–>Basic Functions–>On chip Memory–>RAM:1 PORT , 如图1

图1

双击RAM: 1 PORT ,在跳出的对话框中输入将要生成的单口RAM名称 — s_port_ram, 如 图2,

图2

图3

  • Widths/Blk Type/Clks选择
    • 端口宽度选择,原则上1~256 bits 任意选择, 选择后数据输入data与数据输出q的宽度相同,如果选择M9K结构的片上存储器类型,宽度设成9的整数倍(如1,9,18,27,36等),内部的M9K利用率最高. 这里选择8bit进行测试.
    • 深度选择,选择memory的深度,选择256进行测试
    • 类型: 这里有两种类型可供选择:M9K,LCs, M9K 是FPGA内部的存储器块,在本教材资料部分有专门针对M9K等片上存储器类型的介绍.LC是逻辑单元,实际上就是利用逻辑单元的查找表(也就是常说的分布式存储器),在这里选择Auto类型,让系统工具帮助选择最佳方案.

图4

    • 选择时钟的驱动方式, 可以选择单时钟,即输入、输出共享一个时钟。也可以选择双时钟,即输入、输出的时钟是分开的。但从图4左上角的示意图可以看出,由于地址线(address),data, wren等都是与inclock同步,只有q与outclock同步,因此选择双时钟的意义不大(即使外部使用RAM的程序是双时钟的)。
    • 选择需要寄存的端口,如图5, 由于写入端口(data, wren, address)是必须寄存的,只有q可以选择是否寄存,如果选择非寄存方式,当然也就没有outclock的选择。选择寄存方式可以提升时序收敛效果。
    • 创建时钟使能,可以为每一个寄存的信号创建使能(create one clock enable signal for each clock signal),如address,data,wren,q等,在没有读写需求时,clock信号不能穿透这些信号,同时这些信号也不会反转,因此在没有读写需求时,可以降低功耗。达到低功耗的设计效果。
    • 创建异步清零端,由于data,address ,wren ,rden等信号都是锁存信号,因此在初始化时,它们的值是随机的,提供异步清零可以在初始化时处在一个确定的状态。注意不是对data,address,wren,rden等本身信号清零,而是对寄存器的输出清零。
    • 创建读使能,由于是单口RAM,因此输入输出共享地址address, 如果没有读使能,在写入时,由于地址的变化,也会引起输出端数值的变化,即使外部此时没有读需求,也会引起功耗的上升,因此建议创建rden端,如果在应用程序中不用该使能控制,在例化的程序中将该端直接设为1’b1即可。

图5

    • 在同一个地址上出现了写数据的同时又要读数据,可以提供两种选择:一种是读旧数据(old data)如图6,一种是读取新数据,也就是将写入的数据直接复制到数据输出端口q。这种使用方式在单口RAM作为CPU内存使用时提供了便捷的数据复制方式,如图7。读取新数据时如果提供了MASK信号(当多个字节读时),被屏蔽的信号(字节)以X(无关或未知)值输出。

图6

图7

    • 图8,选择是否加载初始化文件*.mif或*.hex文件,这项设置于ROM的设置相同,可以参照ROM的初始化文件设置。如果没有加载初始化文件,在上电后直接读取RAM中的内容将是0000,或XXXX(如果选择了Initialize memory content data to XXXX on power up in simulation)

图8

    • 为了能正确仿真RAM的功能,需要使用Altera_mf库文件。

图9

    • 如图10, 选择生成的文件,并将生成的文件加入项目中

图10

图11

2. 编写测试文件

  • 编写testbench测试文件,并例化生成的单端口RAM IP核

新建Verilog文件,实现单端口RAM的测试, 测试代码如下:

`timescale 1 ns / 1 ps
module test_ram
(
 
);
 
parameter PERIOD = 20;
 
reg clk;
reg rst;
 
reg [7:0] address;
reg wren;
reg rden;
reg [7:0] data_in;
 
wire [7:0] data_out;
reg [2:0] rd_wr_st;
 
initial 
begin
    clk = 0;
    rst = 1'b1;
    #50 rst = 1'b0;
end
 
 
always #(PERIOD/2) clk = ~clk;
 
always@(posedge clk or posedge rst)
if(rst) 
begin
    address <= 0;
    wren <= 0;
    rden <= 0;
    data_in <= 0;
    rd_wr_st <= 0;
end
else 
begin
    case (rd_wr_st)
    0:
    begin
        address <= 0;
        wren <= 0;
        rden <= 1'b1;
        data_in <= 0;
        rd_wr_st <= 1;
    end
    1:
    begin
        rden <= 1'b1;
        wren <= 0;
        if(address == 255) 
        begin
            data_in <= address + 2;
            address <= 0;
            rden <= 1'b0;
            wren <= 1'b1;
            rd_wr_st <= 2;
        end
        else
            address <= address + 1;
    end
    2:
    begin
        rden <= 1'b0;
        wren <= 1'b1;
        data_in <= address + 2;
        if(address == 255) 
        begin
            address <= 0;
            rden <= 1'b1;
            wren <= 1'b0;
            rd_wr_st <= 1;
        end
        else
            address <= address + 1;
    end
    default: rd_wr_st <= 0;
    endcase
end
 
s_port_ram s_port_ram_inst
(
    .address (address),
    .clock   (clk),
    .data    (data_in),
    .rden    (rden),
    .wren    (wren),
    .q       (data_out)
);
 
endmodule

 

仿真波形如图12,13,14,15

图12 初始化读

图13 写数据-1

图14 写数据2,写后读

从图14,可以看出,由于数据、地址是触发器类型,而且从图5选择输出q也有一级寄存器,因此给出地址address,rden后要延时2个时钟才会有相应的数据输出。

练习题:

  1. 上面的测试例子采用非阻塞式赋值语句,用阻塞式赋值语句改写,观察读写过程。
  2. 将输出q修改为非寄存模式,观察写后读数据过程,与寄存模式有何不同。

 

对应视频:

Posted in FPGA, FPGA 教材教案, Quartus II, Quartus II, Verilog, Verilog, 开发语言, 教材与教案, 文章, 编程语言

发表回复

相关链接