Mapping Direct I/O Streams to SAXI Lite Interface - 2024.2 English - UG1399

Vitis High-Level Synthesis User Guide (UG1399)

Document ID
UG1399
Release Date
2024-11-13
Version
2024.2 English

Mapping Direct I/O variables to AXI4-Lite requires special consideration. This is achieved by using the interface attribute direct_io=true. The generated RTL will contain identical logic to the Direct I/O streams. However, this interface attribute has a simulation limitation, which is explained in the following section.

Limitations and Solutions

The direct_io=true attribute has a limitation: the testbench can only provide one variable per top-level call. This means that during csim/co-simulation, the testbench cannot read or write multiple values to the scalar per top-level call. However, in Vitis, since software emulation (sw_emu) and hardware emulation (hw_emu) work on different threads, the host can read and write multiple values to the Direct I/O variable.

In this example, the base variable is mapped to a Direct I/O interface using the interface attribute direct_io=true.

#include "hls_print.h"
#include "hls_stream.h"
#include "hls_directio.h"
struct pkt {int size, bucket; };
typedef int scalar_t
 
 
void pkt_proc(int *buf, scalar_t &base, hls::stream<pkt> &in, hls::stream<pkt> &out) {
 
#pragma HLS interface ap_none port=base direct_io=true
#pragma HLS interface s_axilite port=base
#pragma HLS interface s_axilite port=buf
#pragma HLS interface ap_memory port=buf depth=32
 
 
  for (int i = 0; i < 16; i++) {
#pragma HLS pipeline II=1
    pkt t = in.read();
    int tmp = base;
    if (tmp && tmp + i + t.bucket < 32) { // do something only if you have buffer, otherwise pass through
      buf[tmp + i + t.bucket] = buf[tmp + i + t.bucket] + t.size;
    }
    out.write(t);
  }
}

Blocking API

The term blocking means that operation stalls until fresh data is available on the Direct I/O channels. The blocking API automatically checks if the Direct I/O variable has any input data.

Blocking Writes

This method .write() writes the value "counter" to the Direct I/O variable "reset_value".

// usage of void write( const T &din)
 
 
dut_top(hls::ap_vld<int> &reset_value,..)
 
...
    reset_value.write(counter);
 
..

The << operator is overloaded such that it can be used in a similar fashion to the stream insertion operators for C++ stream (for example, iostreams and filestreams). The hls::ap_vld/ap_hs/ap_none<> object to be written to is supplied as the left-hand side argument and the value to be written as the right-hand side.

// usage of void operator << (_const T & din) {
 
dut_top(hls::ap_vld<int> &reset_value,..)
 
 
...
    reset_value << counter;
 
..

Blocking Reads

This <var>.read()method reads from the "reset_myCounter" and assigns the values to the variable reset.

// usage of void read(const T & dout)
 
 
void krnl_stream_vdatamover(...
                      hls::ap_hs<int> &reset_myCounter
                      ) {
...
      int reset = reset_myCounter.read();
...
}

The >> operator is overloaded such that it can be used in a similar fashion to the stream insertion operators for C++ stream (for example, iostreams and filestreams). The hls::ap_vld/ap_hs/ap_none<> object to be read is supplied as the LHS argument, and the destination variable is the RHS.

// Usage of void operator >> (const T& rdata) void krnl_stream_vdatamover(..., hls::ap_hs<int> &reset_myCounter ){
...
 reset_myCounter >> reset;
...
}

Non-blocking API

The term non-blocking means that lack of data (or too much data) on the stream does not block the operation of a function or the iteration of a loop. Non-blocking methods return a Boolean value indicating the status of a read or write: true if successful, false otherwise.

Non-blocking Write

This method is supported for ap_hs/ap_vld which attempts to push data into the stream, returning a Boolean true if successful. Otherwise, false is returned.

// usage of bool write_nb(const T_& din)
 
dut_top(hls::ap_vld<int> &reset_value,..)
 
int counter =1;
reset_value.write_nb(counter);

The << operator is overloaded such that it can be used in a similar fashion to the stream insertion operators for C++ stream (for example, iostreams and filestreams). The hls::ap_vld/ap_hs/ap_none<> object to be written to is supplied as the left-hand side argument and the value to be written as the right-hand side.

// usage of void operator << (_const T & din) {
 
dut_top(hls::ap_vld<int> &reset_value,..)
 
 
...
    reset_value << counter;
 
..

Non-blocking Read

This method is supported for ap_hs/ap_vld which attempts to push data into the stream, returning a Boolean true if successful. Otherwise, false is returned.

// Usage of  bool read_nb(__STREAM_T__& dout)
 
 
hls::ap_vld<int> &reset_myCounterint reset =1; reset_myCounter.read_nb(reset);

The >> operator is overloaded such that it can be used in a similar fashion to the stream insertion operators for C++ stream (for example, iostreams and filestreams). The hls::ap_vld/ap_hs/ap_none<> object to be read is supplied as the LHS argument, and the destination variable is the RHS.

// Usage of void operator >> (const T& rdata)
 
 
void krnl_stream_vdatamover(..., hls::ap_hs<int> &reset_myCounter ){
...
 reset_myCounter >> reset;
...
}