fpgaxilinxvivadovivado-hls

HLS: How to separate AXI4 signals


I am trying to write a module that uses the AXI4 streaming protocol to communicate with the previous and next modules. The modules use the following communication signals:

  1. TDATA, which is 16 bits,
  2. TKEEP, which is 2 bits,
  3. TUSER, which is 1 bit,
  4. TVALID, which is 1 bit,
  5. TREADY, which is 1 bit and goes towards the previous module, and
  6. TLAST, which is 1 bit.

These all need to be separate signals. I tried to implement it using the following code:

#include "core.h"

void core_module(hls::stream<ap_axis_str> &input_stream, hls::stream<ap_axis_str> &output_stream){
#pragma HLS INTERFACE axis port=input_stream
#pragma HLS INTERFACE axis port=output_stream
#pragma HLS INTERFACE s_axilite port=return bundle=CTRL
    ap_axis_str strm_val_in;
    ap_axis_str strm_val_out;
    for (int i = 0; i<NDATA; i++){
        strm_val_in = input_stream.read();
        strm_val_out.data = strm_val_in.data * 2;
        strm_val_out.keep = 3;
        strm_val_out.valid = 1;
        strm_val_in.ready = 1;
        strm_val_out.user = ((i%2)==0);
        strm_val_out.last = (i == NDATA-1) ? 1:0;
        output_stream.write(strm_val_out);
    }
}

where the header file is

#ifndef core_h
#define core_h

#include <ap_int.h>
#include <ap_axi_sdata.h>
#include <hls_stream.h>

typedef ap_uint<16> word;

#define NDATA 10

struct ap_axis_str {
    word    data;
    ap_uint<2>    keep;
    bool    user;
    bool    last;
    bool    ready;
    bool    valid;
};

void core_module(hls::stream<ap_axis_str> &input_stream, hls::stream<ap_axis_str> &output_stream);

#endif

The problem is that this doesn't separate the signals. When I synthesise it and run it in the co-simulation (giving it values from 0 to 9), even if the result is what I expect it to be, the waveform produced looks like this: In this image you can see the input (bottom three signals) and output (top three signals). Take not that TREADY is going in the opposite direction.

We can see that TREADY, TVALID, and TDATA are there, but not the other 3. Furthermore, looking at the contents of TDATA (which for some reason are 64 bits) we notice that they contain all the signals. They are the following:

0001000001030000,
0001000000030002,
0001000001030004,
0001000000030006,
...
000100000003000c, (they are in base 16)
0001000001030010,
0001000100030012.

From which we can see that the 3 in position 12 is probably what was intended to be TKEEP, the 1 in position 8 which only appears in the last case is probably what was intended to be TUSER, the last 4 digits are what was supposed to be TDATA, etc. Additionally, the program drops TREADY when it isn't ready to receive data, which is what is intended of TREADY, but I didn't program it to work this way, which means that it's automatically generated and probably has nothing to do with the TREADY I told it to have.

So my question is: How do I make a module that gives out the correct 6 separate signals for the version of the AXI4 protocol that we are using?


Solution

  • Well, according to the Xilinx Documentation,

    If you specify an hls::stream object with a data type other than ap_axis or ap_axiu, the tool will infer an AXI4-Stream interface without the TLAST signal, or any of the side-channel signals. This implementation of the AXI4-Stream interface consumes fewer device resources, but offers no visibility into when the stream is ending.

    Now I had already imported the needed module with#include <ap_axi_sdata.h>, all I needed to do was actually use it by removing

    struct ap_axis_str {
        word    data;
        ap_uint<2>    keep;
        bool    user;
        bool    last;
        bool    ready;
        bool    valid;
    };
    

    and replacing it with

    typedef ap_axiu<16, 1, 0, 0> ap_axis_str;
    

    Additionally, I needed to remove my manual attempt to control TREADY and TVALID, as those are done automatically.