欢迎您访问 最编程 本站为您分享编程语言代码,编程技术文章!
您现在的位置是: 首页

XILINX UltraScale+ 系列 FPGA/SoC 中的 UltraRAM

最编程 2024-03-27 20:39:49
...

目录

  • 前言
  • 一、UltraRAM的特点
  • 二、UltraRAM的应用场景
  • 三、与Block RAM的比较
  • 四、简单的测试


前言

XILINX UltraRAM的简单介绍,详情见官方文档: ug573-ultrascale-memory-resources.


一、UltraRAM的特点

1、位宽72b,深度4Kb

每块位宽和深度是固定的,不像Block RAM那样可以配置。
但是存储空间更大,每块是288Kb。

2、双端口,单时钟

两个端口使用同一个时钟,不能用于跨时钟域处理。
每个端口可以在每个时钟周期内独立执行一个读操作或一个写操作,但其内部使用的是单端口存储单元。双端口操作是通过在一个单周期内先执行端口A操作,然后执行端口B操作来实现的。

3、在上电和复位期间内存初始化为全0

用户不能自定义初始值

二、UltraRAM的应用场景

在这里插入图片描述
图片来自官方介绍:在 UltraScale+ 器件中使用 UltraRAM

三、与Block RAM的比较

在这里插入图片描述

四、简单的测试

如何使用UltraRAM:

在这里插入图片描述
此处使用第二种方式:XPM

首先在Language Templates中找到XPM_MEMORY

在这里插入图片描述
这里选择常用的SDP RAM测试,选中,ctrl+c,ctrl+v。

写一个简单的生成地址和测试数据的module

`timescale 1ns / 1ps

module ram_ctrl
#(
   parameter DATA_WIDTH = 72    ,
   parameter DATA_DEPTH = 4096
)
(
    output  reg  [DATA_WIDTH         - 1 : 0]  dina    ,
    output  reg  [$clog2(DATA_DEPTH) - 1 : 0]  addra   ,
    output  reg  [$clog2(DATA_DEPTH) - 1 : 0]  addrb   ,
    output                                     wea     ,
    input                                      rst_n   ,
    input                                      clk
    );

    assign wea = 1'b1;

    //输入地址
    always @(posedge clk or negedge rst_n) begin
        if(!rst_n)begin
            addra <= 'd0;
        end
        else begin
            addra <= addra + 'd1;
        end
    end

    //输入数据
    always @(posedge clk or negedge rst_n) begin
        if(!rst_n)begin
            dina <= 'd0;
        end
        else begin
            dina <= dina +'b1;
        end
    end

    //输出地址
    always @(posedge clk or negedge rst_n) begin
        if(!rst_n)begin
            addrb <= 'd0;
        end
        else begin
            addrb <= addra;
        end
    end

endmodule

顶层:

`timescale  1ns / 1ps

module sdp_ram_top
#(
   parameter DATA_WIDTH =  72     ,
   parameter DATA_DEPTH =  4096
)
(
   output  [DATA_WIDTH - 1 : 0]  doutb ,  
   input                         rst_n ,
   input                         clk   
   );

   parameter ADDR_WIDTH = $clog2(DATA_DEPTH);

   wire  [DATA_WIDTH - 1 : 0]  dina  ;
   wire  [ADDR_WIDTH - 1 : 0]  addra ;
   wire  [ADDR_WIDTH - 1 : 0]  addrb ;
   wire                        wea   ;

   wire clka, clkb;
   assign clka = clk;
   assign clkb = clk;

   ram_ctrl #(
      .DATA_WIDTH ( DATA_WIDTH ),
      .DATA_DEPTH ( DATA_DEPTH )
   )
   u_ram_ctrl (
      .rst_n                   ( rst_n ),
      .clk                     ( clk   ),

      .dina                    ( dina  ),
      .addra                   ( addra ),
      .addrb                   ( addrb ),
      .wea                     ( wea   )
   );

   xpm_memory_sdpram #(
      .ADDR_WIDTH_A(ADDR_WIDTH),               // DECIMAL
      .ADDR_WIDTH_B(ADDR_WIDTH),               // DECIMAL
      .AUTO_SLEEP_TIME(0),            // DECIMAL
      .BYTE_WRITE_WIDTH_A(DATA_WIDTH),        // DECIMAL
      .CASCADE_HEIGHT(0),             // DECIMAL
      .CLOCKING_MODE("common_clock"), // String
      .ECC_MODE("no_ecc"),            // String
      .MEMORY_INIT_FILE("none"),      // String
      .MEMORY_INIT_PARAM("0"),        // String
      .MEMORY_OPTIMIZATION("true"),   // String
      .MEMORY_PRIMITIVE("ultra"),      // String
      .MEMORY_SIZE(294912),             // DECIMAL
      .MESSAGE_CONTROL(0),            // DECIMAL
      .READ_DATA_WIDTH_B(DATA_WIDTH),         // DECIMAL
      .READ_LATENCY_B(2),             // DECIMAL
      .READ_RESET_VALUE_B("0"),       // String
      .RST_MODE_A("SYNC"),            // String
      .RST_MODE_B("SYNC"),            // String
      .SIM_ASSERT_CHK(0),             // DECIMAL; 0=disable simulation messages, 1=enable simulation messages
      .USE_EMBEDDED_CONSTRAINT(0),    // DECIMAL
      .USE_MEM_INIT(1),               // DECIMAL
      .WAKEUP_TIME("disable_sleep"),  // String
      .WRITE_DATA_WIDTH_A(DATA_WIDTH),        // DECIMAL
      .WRITE_MODE_B("read_first")      // String
   )
   xpm_memory_sdpram_inst (
      .dbiterrb( ),             // 1-bit output: Status signal to indicate double bit error occurrence
                                       // on the data output of port B.

      .doutb(doutb),                   // READ_DATA_WIDTH_B-bit output: Data output for port B read operations.
      .sbiterrb( ),             // 1-bit output: Status signal to indicate single bit error occurrence
                                       // on the data output of port B.

      .addra(addra),                   // ADDR_WIDTH_A-bit input: Address for port A write operations.
      .addrb(addrb),                   // ADDR_WIDTH_B-bit input: Address for port B read operations.
      .clka(clka),                     // 1-bit input: Clock signal for port A. Also clocks port B when
                                       // parameter CLOCKING_MODE is "common_clock".

      .clkb(clkb),                     // 1-bit input: Clock signal for port B when parameter CLOCKING_MODE is
                                       // "independent_clock". Unused when parameter CLOCKING_MODE is
                                       // "common_clock".

      .dina(dina),                     // WRITE_DATA_WIDTH_A-bit input: Data input for port A write operations.
      .ena(1'b1),                       // 1-bit input: Memory enable signal for port A. Must be high on clock
                                       // cycles when write operations are initiated. Pipelined internally.

      .enb(1'b1),                       // 1-bit input: Memory enable signal for port B. Must be high on clock
                                       // cycles when read operations are initiated. Pipelined internally.

      .injectdbiterra(1'b0), // 1-bit input: Controls double bit error injection on input data when
                                       // ECC enabled (Error injection capability is not available in
                                       // "decode_only" mode).

      .injectsbiterra(1'b0), // 1-bit input: Controls single bit error injection on input data when
                                       // ECC enabled (Error injection capability is not available in
                                       // "decode_only" mode).

      .regceb(1'b1),                 // 1-bit input: Clock Enable for the last register stage on the output
                                       // data path.

      .rstb(1'b0),                     // 1-bit input: Reset signal for the final port B output register stage.
                                       // Synchronously resets output port doutb to the value specified by
                                       // parameter READ_RESET_VALUE_B.

      .sleep(1'b0),                   // 1-bit input: sleep signal to enable the dynamic power saving feature.
      .wea(wea)                        // WRITE_DATA_WIDTH_A/BYTE_WRITE_WIDTH_A-bit input: Write enable vector
                                       // for port A input data port dina. 1 bit wide when word-wide writes are
                                       // used. In byte-wide write configurations, each bit controls the
                                       // writing one byte of dina to address addra. For example, to
                                       // synchronously write only bits [15-8] of dina when WRITE_DATA_WIDTH_A
                                       // is 32, wea would be 4'b0010.

   );

endmodule

testbench:

`timescale  1ns / 1ps

module tb_sdp_ram_top;

// sdp_ram_top Parameters
parameter PERIOD      = 10               ;
parameter DATA_WIDTH  = 72               ;
parameter DATA_DEPTH  = 4096             ;

// sdp_ram_top Inputs
reg   rst_n                                = 0 ;
reg   clk                                  = 0 ;

// sdp_ram_top Outputs
wire  [DATA_WIDTH - 1 : 0]  doutb          ;


initial
begin
    forever #(PERIOD/2)  clk=~clk;
end

initial
begin
    #(PERIOD*2) rst_n  =  1;
end

sdp_ram_top #(
    .DATA_WIDTH ( DATA_WIDTH ),
    .DATA_DEPTH ( DATA_DEPTH )
)
u_sdp_ram_top (
    .rst_n                   ( rst_n ),
    .clk                     ( clk   ),

    .doutb                   ( doutb )
);

endmodule

仿真结果:
在这里插入图片描述
可以看到数据输出延迟了2个clk,对应例化RAM时的

.READ_LATENCY_B(2), 

使用URAM或者BRAM时这个值至少为1,代表输出锁存,为2及以上时代表使用了输出寄存器。
使用DRAM时这个值可以为0,因为DRAM底层为组合逻辑。

综合后可以看到占用的资源:

在这里插入图片描述
4K*72的内存仅用了一个URAM。

.MEMORY_PRIMITIVE("ultra"), 

改为

.MEMORY_PRIMITIVE("block"), 

即使用Block RAM实现,综合后:

在这里插入图片描述
使用了8个BRAM(72x512x8)。

ila调试:

在这里插入图片描述
和仿真结果是一样的。

最后尝试一下给URAM设置初始值

在这里插入图片描述
果然不行。