Port-Level I/O: Memory Interface Protocol - 2023.2 English

Vitis High-Level Synthesis User Guide (UG1399)

Document ID
Release Date
2023.2 English

Array arguments are implemented by default as an ap_memory interface using word-addressing. This is a standard block RAM interface with data, address, chip-enable, and write-enable ports.

An ap_memory interface can be implemented as a single-port of dual-port interface. If Vitis HLS can determine that using a dual-port interface will reduce the initial interval, it will automatically implement a dual-port interface. The BIND_STORAGE pragma or directive can be used to specify the memory resource and if this directive is specified on the array with a single-port block RAM, a single-port interface will be implemented. Conversely, if a dual-port interface is specified using the BIND_STORAGE pragma and Vitis HLS determines this interface provides no benefit it will automatically implement a single-port interface.

If the array is accessed in a sequential manner an ap_fifo interface can be used. As with the ap_hs interface, Vitis HLS will halt if it determines the data access is not sequential, report a warning if it cannot determine if the access is sequential or issue no message if it determines the access is sequential. The ap_fifo interface can only be used for reading or writing, not both.

ap_memory, bram

The ap_memory and bram interface port-level I/O protocols are used to implement array arguments. This type of port-level I/O protocol can communicate with memory elements (for example, RAMs and ROMs) when the implementation requires random accesses to the memory address locations.

Note: If you only need sequential access to the memory element, use the ap_fifo interface instead. The ap_fifo interface reduces the hardware overhead, because address generation is not performed.

The ap_memory and bram interface port-level I/O protocols are similar, though not the same. The ap_memory interface uses word-based addressing, and generates an additional chip enable control signal, while the bram interface uses byte-addressing. The following table summarizes the differences:

Table 1. ap_memory vs. bram
  ap_memory bram
Address of the nth word n*1 n*(word-size in bytes)
Address bit-width ceil(log2(depth)) 32 bit
Supported word size arbitrary 8*power-of-two bits
Byte-enable support Yes, if word size is multiple of bytes Yes
IP integrator Support Not-available Supported by Block Memory Generator and/or Embedded Memory Generator

In the Vivado tool, the way the interfaces are represented is also different:

  • The ap_memory interface appears as discrete ports.
  • The bram interface appears as a single, grouped port. In IP integrator, you can use a single connection to create connections to all ports.

When using a memory interface, specify the implementation using the BIND_STORAGE pragma. If no target is specified for the arrays, Vitis HLS determines whether to use a single or dual-port RAM interface.

Tip: Before running synthesis, ensure array arguments are targeted to the correct memory type using the BIND_STORAGE pragma. Re-synthesizing with corrected memories can result in a different schedule and RTL.

The following figure shows an array named d specified as a single-port block RAM. The port names are based on the C/C++ function argument. For example, if the C/C++ argument is d, the chip-enable is d_ce, and the input data is d_q0 based on the output/q port of the block RAM.

Figure 1. Behavior of ap_memory Interface

After reset, the following occurs:

  • After start is applied, the block begins normal operation.
  • Reads are performed by applying an address on the output address ports while asserting the output signal d_ce.
    Note: For a default block RAM, the design expects the input data d_q0 to be available in the next clock cycle. You can use the BIND_STORAGE pragma to indicate the RAM has a longer read latency.
  • Write operations are performed by asserting output ports d_ce and d_we while simultaneously applying the address and output data d_d0.


When an output port is written to, its associated output valid signal interface is the most hardware-efficient approach when the design requires access to a memory element and the access is always performed in a sequential manner, that is, no random access is required. The ap_fifo port-level I/O protocol supports the following:

  • Allows the port to be connected to a FIFO
  • Enables complete, two-way empty-full communication
  • Works for arrays, pointers, and pass-by-reference argument types
Note: Functions that can use an ap_fifo interface often use pointers and might access the same variable multiple times. To understand the importance of the volatile qualifier when using this coding style, see Multi-Access Pointers on the Interface.

In the following example, in1 is a pointer that accesses the current address, then two addresses above the current address, and finally one address below.

void foo(int* in1, ...) {
 int data1, data2, data3;  
 data1= *in1; 
 data2= *(in1+2);
 data3= *(in1-1);

If in1 is specified as an ap_fifo interface, Vitis HLS checks the accesses, determines the accesses are not in sequential order, issues an error, and halts. To read from non-sequential address locations, use an ap_memory or bram interface.

You cannot specify an ap_fifo interface on an argument that is both read from and written to. You can only specify an ap_fifo interface on an input or an output argument. A design with input argument in and output argument out specified as ap_fifo interfaces behaves as shown in the following figure.

Figure 2. Behavior of ap_fifo Interface

For inputs, the following occurs:

  • After ap_start is applied, the block begins normal operation.
  • If the input port is ready to be read but the FIFO is empty as indicated by input port in_empty_n Low, the design stalls and waits for data to become available.
  • When the FIFO contains data as indicated by input port in_empty_n High, an output acknowledge in_read is asserted High to indicate the data was read in this cycle.

For outputs, the following occurs:

  • After start is applied, the block begins normal operation.
  • If an output port is ready to be written to but the FIFO is full as indicated by out_full_n Low, the data is placed on the output port but the design stalls and waits for the space to become available in the FIFO.
  • When space becomes available in the FIFO as indicated by out_full_n High, the output acknowledge signal out_write is asserted to indicate the output data is valid.
  • If the top-level function or the top-level loop is pipelined using the -rewind option, Vitis HLS creates an additional output port with the suffix _lwr. When the last write to the FIFO interface completes, the _lwr port goes active-High.