• IP核——RAM


    一、Quartus

    1.打开Quartus ii,点击Tools---MegaWizard Plug-In Manager

    2.弹出创建页面,选择Creat a new custom megafunction variation,点Next

     

    3.选择IP核,可以直接搜索ram,选择RAM:2-PORT,右上方选择器件型号,语言选成Verilog,再填写一下路径名字,点Next,后面就是参数设置了。

    4.设置读写需要几个端口,深度计算按word还是bit。Next

    5.设置深度,位宽,类型。Next

    6.设置读写需要几个端口,深度计算按word还是bit,一般选word。Next

    7.是否为输出添加一个寄存器(加了寄存器可以使RAM输出的数据更稳定)?本来ram的输出就是会慢1clk,勾选后又慢1clk,所以一般不勾选。Next

    8.输出的是新数据还是老数据,一般是要新数据,所以I don't care就行。Next

    9.是否添加初始化文件mif ? Next

    10.告诉你此IP核的编译库是什么,Next

    11.输出的文件列表,除了正常IP核,还可以选择例化文件,注意bb.v文件用不到,一般是不勾选的。之后点finish就生成IP核了。

    二、ISE

    1.创建ISE工程,IP核需要在ISE工程里面进行调用。点击Tools---Core Generator...

    2.在新弹出来的界面中创建一个属于IP核的工程:file---new project,并填写文件存储位置和文件名称,一般为ipcore_dir文件夹,点击保存

    3.弹出的Part处填写器件的系列、型号、封装以及速度等级,Generation处设置语言为Verilog,点击OK

    4.点击文件夹,找到Memories & Storage Elements---RAMs & ROMs---Block Memory Generator,(也可以直接搜索)双击打开,进行参数设置

    5.设置模块名称,Next

    6.类型选择,一般选Single Dual RAM,该RAM为“a口负责写,b口负责读”,而对于真双口RAM来说,a和b都是可读可写。其他选项根据需要勾选。Next

    7.RAM的位宽、深度、使能选择,Next

     

     8.是否在B端添加一个寄存器(加了寄存器可以使RAM输出的数据更稳定)?本来ram的输出就慢1clk,勾选了又慢1clk,所以一般不勾选。是否需要初始化并加载初始化ceo文件,Next

     9.是否对B口添加复位键,Next

     10.总结页面,注意里面一句话:B口的读取有1clk的延迟,点击Generate即可生成RAM。大功告成!

    三、单RAM读写操作(by威三学院FPGA教程)

    本代码针对Quartus的RAM:2-PORT

     1 //==========================================================================
     2 // --- 名称 : ram_ctrl.v
     3 // --- 作者 : xianyu_FPGA
     4 // --- 日期 : 2019-06-23
     5 // --- 描述 : 单个ram读写操作
     6 //==========================================================================
     7 
     8 module ram_ctrl
     9 //---------------------<端口声明>-------------------------------------------
    10 (
    11 input  wire             clk                 , //时钟,50Mhz
    12 input  wire             rst_n               , //复位,低电平有效
    13 input  wire [7:0]       din                 , //写入的数据
    14 output wire [7:0]       dout                  //读出的数据
    15 );
    16 //---------------------<信号定义>-------------------------------------------
    17 reg                     wr_en               ; //写使能
    18 reg  [7:0]              wr_addr             ; //写地址
    19 reg  [7:0]              rd_addr             ; //读地址
    20 
    21 //--------------------------------------------------------------------------
    22 //--   ram例化
    23 //--------------------------------------------------------------------------
    24 ram_256x8 u_ram_256x8
    25 (
    26     .clock              (clk                ),
    27     .wren               (wr_en              ),
    28     .wraddress          (wr_addr            ),
    29     .data               (din                ),
    30     .rdaddress          (rd_addr            ),
    31     .q                  (dout               )
    32 );
    33 
    34 //--------------------------------------------------------------------------
    35 //--   产生写使能
    36 //--------------------------------------------------------------------------
    37 always @(posedge clk or negedge rst_n) begin
    38     if(!rst_n)
    39         wr_en <= 1'b1;
    40     else if(wr_addr==8'd255)
    41         wr_en <= 1'b0;
    42     else if(rd_addr==8'd255)
    43         wr_en <= 1'b1;
    44 end
    45 
    46 //--------------------------------------------------------------------------
    47 //--   产生写地址
    48 //--------------------------------------------------------------------------
    49 always @(posedge clk or negedge rst_n) begin
    50     if(!rst_n)
    51         wr_addr <= 8'd0;
    52     else if(wr_en==1)
    53         wr_addr <= wr_addr+1'd1;
    54     else
    55         wr_addr <= 8'd0;
    56 end
    57 
    58 //--------------------------------------------------------------------------
    59 //--   产生读地址
    60 //--------------------------------------------------------------------------
    61 always @(posedge clk or negedge rst_n) begin
    62     if(!rst_n)
    63         rd_addr <= 8'd0;
    64     else if(wr_en==0)
    65         rd_addr <= rd_addr+1'd1;
    66     else
    67         rd_addr <= 8'd0;
    68 end
    69 
    70 
    71 
    72 endmodule

    四、双RAM乒乓操作(by威三学院FPGA教程

    RAM的简单读写比较简单,平常也用不太到,比较重要的是异步双口RAM实现乒乓操作。

    本代码针对ise的Simple Dual PORT RAM

      1 //==========================================================================
      2 // --- 名称 : ram_pp.v
      3 // --- 作者 : xianyu_FPGA
      4 // --- 日期 : 2019-06-23
      5 // --- 描述 : 异步双口ram乒乓操作
      6 //==========================================================================
      7 
      8 module ram_pp
      9 //---------------------<端口声明>-------------------------------------------
     10 (
     11 input  wire             clk                 , //时钟,50Mhz
     12 input  wire             rst_n               , //复位,低电平有效
     13 input  wire [7:0]       din                 , //输入的数据
     14 output wire [7:0]       dout                  //输出的数据
     15 );
     16 //---------------------<信号定义>-------------------------------------------
     17 //ram_a
     18 reg                     wr_en_a             ;
     19 reg     [9:0]           wr_addr_a           ;
     20 reg     [9:0]           rd_addr_a           ;
     21 wire    [7:0]           dout_a              ;
     22 //ram_b
     23 reg                      wr_en_b            ;
     24 reg     [9:0]            wr_addr_b          ;
     25 reg     [9:0]            rd_addr_b          ;
     26 wire    [7:0]            dout_b             ;
     27 //缓一拍
     28 reg                      wr_en_a_r0         ;
     29 
     30 //--------------------------------------------------------------------------
     31 //--   ip核例化
     32 //--------------------------------------------------------------------------
     33 //ram_a
     34 ram_1024x8 u_ram_1024x8_a
     35 (
     36   .clka                 (clk                ), // input  clka
     37   .wea                  (wr_en_a            ), // input  [0 : 0] wea
     38   .addra                (wr_addr_a          ), // input  [9 : 0] addra
     39   .dina                 (din                ), // input  [7 : 0] dina
     40   .clkb                 (clk                ), // input  clkb
     41   .addrb                (rd_addr_a          ), // input  [9 : 0] addrb
     42   .doutb                (dout_a             )  // output [7 : 0] doutb
     43 );
     44 //ram_b
     45 ram_1024x8 u_ram_1024x8_b
     46 (
     47   .clka                 (clk                ), // input  clka
     48   .wea                  (wr_en_b            ), // input  [0 : 0] wea
     49   .addra                (wr_addr_b          ), // input  [9 : 0] addra
     50   .dina                 (din                ), // input  [7 : 0] dina
     51   .clkb                 (clk                ), // input  clkb
     52   .addrb                (rd_addr_b          ), // input  [9 : 0] addrb
     53   .doutb                (dout_b             )  // output [7 : 0] doutb
     54 );
     55 
     56 //--------------------------------------------------------------------------
     57 //--   ram_a
     58 //--------------------------------------------------------------------------
     59 // 写使能
     60 always @(posedge clk or negedge rst_n) begin
     61     if(!rst_n)
     62         wr_en_a <= 1'b1;
     63     else if(rd_addr_a=='d1023)
     64         wr_en_a <= 1'b1;
     65     else if(wr_addr_a=='d1023)
     66         wr_en_a <= 1'b0;
     67 end
     68 
     69 // 写地址
     70 always @(posedge clk or negedge rst_n) begin
     71     if(!rst_n)
     72         wr_addr_a <= 'd0;
     73     else if(wr_addr_a=='d1023)
     74         wr_addr_a <= 'd0;
     75     else if(wr_en_a==1'b1)
     76         wr_addr_a <= wr_addr_a + 1'b1;
     77 end
     78 
     79 // 读地址
     80 always @(posedge clk or negedge rst_n) begin
     81     if(!rst_n)
     82         rd_addr_a <= 'd0;
     83     else if(rd_addr_a=='d1023)
     84         rd_addr_a <= 'd0;
     85     else if(wr_en_a==1'b0)
     86         rd_addr_a <= rd_addr_a + 1'b1;
     87 end
     88 
     89 //--------------------------------------------------------------------------
     90 //--   ram_b
     91 //--------------------------------------------------------------------------
     92 // 写使能
     93 always @(posedge clk or negedge rst_n) begin
     94     if(!rst_n)
     95         wr_en_b <= 1'b0;
     96     else if(wr_addr_a=='d1023)    //关键之处!!!
     97         wr_en_b <= 1'b1;
     98     else if(wr_addr_b=='d1023)
     99         wr_en_b <= 1'b0;
    100 end
    101 
    102 // 写地址
    103 always @(posedge clk or negedge rst_n) begin
    104     if(!rst_n)
    105         wr_addr_b <= 'd0;
    106     else if(wr_addr_b=='d1023)
    107         wr_addr_b <= 'd0;
    108     else if(wr_en_b==1'b1)
    109         wr_addr_b <= wr_addr_b + 1'b1;
    110 end
    111 
    112 // 读地址
    113 always @(posedge clk or negedge rst_n) begin
    114     if(!rst_n)
    115         rd_addr_b <= 'd0;
    116     else if(rd_addr_b=='d1023)
    117         rd_addr_b <= 'd0;
    118     else if(wr_en_b==1'b0)
    119         rd_addr_b <= rd_addr_b + 1'b1;
    120 end
    121 
    122 //--------------------------------------------------------------------------
    123 //--   wr_en_a缓一拍时序才对齐
    124 //--------------------------------------------------------------------------
    125 always @(posedge clk or negedge rst_n) begin
    126     if(!rst_n)
    127         wr_en_a_r0 <= 0;
    128     else
    129         wr_en_a_r0 <= wr_en_a;
    130 end
    131 
    132 //--------------------------------------------------------------------------
    133 //--   数据输出
    134 //--------------------------------------------------------------------------
    135 assign dout = (wr_en_a_r0==0) ? dout_a : dout_b;
    136 
    137 
    138 
    139 endmodule

    五、单口,伪双口,真双口的区别

    1.单口:只有一组数据线和地址线,只生成一个addr,因此不能同时进行读写。

    2.双口:拥有两组数据线和地址线,可以生成wr_addr和rd_addr,因此可以同时进行读写。

        ①伪双口:一个端口读,一个端口写。

        ②真双口:两个端口都能读能写。

    ps:

    无论是单口、伪双口还是真双口,他们都只使用一块Memory,真双口其实是两组地址对同一块Memory进行读写,如果真双口的两端口同时对同一地址进行写入数据,那实际情况是未知(仿真也不可信)。

    六、ROM、RAM和FIFO的区别

    1.ROM有地址,只能读而不能写。用初始化文件mif/ceo将内容存进去,读取不会使得数据减少消失。

    2.RAM有地址,可以进行寻址读写,数据写进去后,读取不会使得数据减少消失。

    3.FIFO没有地址,只能是先进先出,数据写进去后,读取会使得数据减少消失,读一个少一个。

    参考资料:

    [1]威三学院FPGA教程

    [2]小梅哥FPGA教程

  • 相关阅读:
    oracle 处理找被删掉且提交了事务的数据
    java去除下划线并首字母大写
    假数据仓库-常见数据枚举(日期、月份、周几、星期几,前导零、Excel 列号)
    自然语言处理标注工具——Brat(安装、测试、使用)
    判断当前点击位置在不在某个区域内
    java调用C#程序集
    UE使用EditorUtilityWidget完成简单的编辑器内工具
    CodeForces 230B
    mac中安装启动使用jmeter步骤
    Ubuntu中samba配置过程
  • 原文地址:https://www.cnblogs.com/xianyufpga/p/11073798.html
Copyright © 2020-2023  润新知