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;
...
}