URAM usage instructions: xpm_memory_tdpram primitive calls uram

1, xpm_memory_tdpram primitive calls uram, and parameter description

This primitive uses something similar to a true dual-port RAM. Memory can be read from and written to via port A and port B simultaneously.

The basic structure of xpm_memory_tdpram is shown in the figure below

For ease of use, I instantiate this primitive. The following is an example of how I use this primitive to call uram. This example introduces common interfaces and uses them similar to ordinary ram.

module uram
#(
    parameter p_wdith_addr_a = 19 ,
    parameter p_wdith_wdata = 72 ,
    parameter p_wdith_addr_b = 19 ,
    parameter p_wdith_rdata = 72,
    parameter p_memory_size = 524287* 72,
    parameter p_read_delay = 13 ,
    parameter p_byte_with = 8 ,
    parameter p_wea_with = 27 ,
    parameter p_web_with = 1
)
(
    inputclk,
    input rst_n ,
    //port a
    input [ p_wea_with-1 : 0 ] wea ,
    input [ p_wdith_wdata-1 : 0 ] dina ,

    input ena,
    input [ p_wdith_addr_a-1 : 0 ] addra ,
    output [ p_wdith_rdata-1 : 0 ] douta,
    // port b
    input [ p_web_with-1 : 0 ] web ,
    input [ p_wdith_addr_b-1 : 0 ] addrb ,
    input [ p_wdith_wdata-1 : 0 ] dinb,

    input enb,
    output [ p_wdith_rdata-1 : 0 ] doutb



 );





// xpm_memory_tdpram: In order to incorporate this function into the design,
// Verilog: the following instance declaration needs to be placed
// instance : in the body of the design code. The instance name
// declaration : (xpm_memory_tdpram_inst) and/or the port declarations within the
// code: parenthesis may be changed to properly reference and
// : connect this function to the design. All inputs
// : and outputs must be connected.

// Please reference the appropriate libraries guide for additional information on the XPM modules.

// <-----Cut code below this line---->

   // xpm_memory_tdpram: True Dual Port RAM
   // Xilinx Parameterized Macro, version 2019.1

   xpm_memory_tdpram #(
      .ADDR_WIDTH_A (p_wdith_addr_a ), // DECIMAL
      .ADDR_WIDTH_B (p_wdith_addr_b ), // DECIMAL
      .AUTO_SLEEP_TIME (0 ), // DECIMAL
      .BYTE_WRITE_WIDTH_A (p_byte_with ), // DECIMAL
      .BYTE_WRITE_WIDTH_B (p_wdith_rdata ), // 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 (p_memory_size ), // DECIMAL
      .MESSAGE_CONTROL (0 ), // DECIMAL
      .READ_DATA_WIDTH_A (p_wdith_rdata ), // DECIMAL
      .READ_DATA_WIDTH_B (p_wdith_rdata ), // DECIMAL
      .READ_LATENCY_A (p_read_delay ), // DECIMAL
      .READ_LATENCY_B (p_read_delay ), // DECIMAL
      .READ_RESET_VALUE_A ("0" ), // String
      .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 (p_wdith_wdata ), // DECIMAL
      .WRITE_DATA_WIDTH_B (p_wdith_wdata ), // DECIMAL
      .WRITE_MODE_A ("no_change" ), // String
      .WRITE_MODE_B ("no_change" ) // String
   )
   xpm_memory_tdpram_inst (
      .dbiterra ( ), // 1-bit output: Status signal to indicate double bit error occurrence
                                                   // on the data output of port A.

      .dbiterrb ( ), // 1-bit output: Status signal to indicate double bit error occurrence
                                                   // on the data output of port A.

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

      .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 and read operations.
      .addrb (addrb ), // ADDR_WIDTH_B-bit input: Address for port B write and read operations.
      .clka (clk ), // 1-bit input: Clock signal for port A. Also clocks port B when
                                                   // parameter CLOCKING_MODE is "common_clock".

      .clkb (clk ), // 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.
      .dinb (dinb), // WRITE_DATA_WIDTH_B-bit input: Data input for port B write operations.
      .ena (ena ), // 1-bit input: Memory enable signal for port A. Must be high on clock
                                                   // cycles when read or write operations are initiated. Pipelined
                                                   // internally.
      .enb (enb ), // 1-bit input: Memory enable signal for port B. Must be high on clock
                                                   // cycles when read or write 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).

      .injectdbiterrb (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).

      .injectsbiterrb (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).

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

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

      .rsta (~rst_n ), // 1-bit input: Reset signal for the final port A output register stage.
                                                    // Synchronously resets output port douta to the value specified by
                                                    // parameter READ_RESET_VALUE_A.

      .rstb (~rst_n ), // 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-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 write synchronously
                                                    // only bits [15-8] of dina when WRITE_DATA_WIDTH_A is 32, wea would be
                                                    // 4'b0010.

      .web (web) // WRITE_DATA_WIDTH_B-bit input: Write enable vector for port B input
                                                    // data port dinb. 1 bit wide when word-wide writes are used. In
                                                    // byte-wide write configurations, each bit controls the writing one
                                                    // byte of dinb to address addrb. For example, to write synchronously
                                                    // only bits [15-8] of dinb when WRITE_DATA_WIDTH_B is 32, web would be
                                                    // 4'b0010.

   );

Some parameter descriptions about this primitive

For ease of use, I have quoted the relevant parameters of uram. Since port a and port b are independent of each other, only the parameters related to port a are introduced here (the same applies to port b).

ADDR_WIDTH_A: Address (addra) bit width, ranging from 1 to 20

READ_DATA_WIDTH_A: Read data (douta) bit width, must be an integer multiple of 72

WRITE_DATA_WIDTH_A: The write data (dina) bit width is equal to READ_DATA_WIDTH_A

MEMORY_SIZE: The size of the calling ram, equal to (2^ ADDR_WIDTH_A)* READ_DATA_WIDTH_A

BYTE_WRITE_WIDTH_A: This option will determine the bit width of wea. It is recommended to set it to 8 or equal to READ_DATA_WIDTH_A based on actual use.

Description: wea bit width is equal to READ_DATA_WIDTH_A / BYTE_WRITE_WIDTH_A

The function of wea is to control which specific bits of dina are written.

Take READ_DATA_WIDTH_A =72 and BYTE_WRITE_WIDTH_A=8 as an example. At this time, the bit width of wea is 9 bits.

When wea== 9b000_000_001 only dina[7:0] can be written,

When wea== 9b000_000_111 only dina[23:0] can be written,

dina[71:0] can be written when wea== 9b111_111_111.

(Note: Writing is only valid when ena and wea are in effect)

READ_LATENCY_A: Range 0~100, how many clock cycles the output is delayed. When the read operation is valid, the data will be read out after the number of clock cycles. (The specific settings will be explained in the notes)

The timing sequence in the figure below is a simple read and write timing sequence with READ_LATENCY_A equal to 10 and wea bit width 1

2. Precautions and other instructions when calling uram

1. Regarding the specific settings of READ_LATENCY_A, the official recommendation is

When the called uram resource is greater than 2MB, the output delay must be greater than 8 clock cycles, because the default cascade height used by the Vivado synthesizer is 8. However, in actual use, the delay should be larger the more uram resources are called, otherwise an error will be reported during the synthesis process, not just equal to 9 or 10 clock cycles. It is recommended to use 9 as the base. For every additional 2MB of uram resource called, the delay will be one more clock cycle.

2. Regarding whether BYTE_WRITE_WIDTH_A can be set to other values than 8 and READ_DATA_WIDTH_A

Theoretically, it can be set to any value from 1 to READ_DATA_WIDTH_A. After the author’s own testing, any value can be set during simulation. However, when used in actual projects, synthesis can only be successful when 1 and READ_DATA_WIDTH_A are set. The specific reason is unknown.

3. When designing, try to avoid the A and B ports from writing to an address at the same time.

4. URAM does not support reset and clearing. To clear data, you need to write 0 to each address.

5. When there are too many uram resources being called, you can try to split a primitive into multiple primitives for control to reduce fanout and optimize timing.

6. If you want to know more about other parameters, you can browse page 182 of xilinx document ug974.