UPGRADE YOUR BROWSER

We have detected your current browser version is not the latest one. Xilinx.com uses the latest web technologies to bring you the best online experience possible. Please upgrade to a Xilinx.com supported browser:Chrome, Firefox, Internet Explorer 11, Safari. Thank you!

cancel
Showing results for 
Search instead for 
Did you mean: 
Visitor agb3
Visitor
6,510 Views
Registered: ‎07-01-2016

HLS-generated IP doesn't start

I'm trying to make a IP block in HLS which will control an AXI DataMover. I've been working with the Vivado debugger, but I'm completely puzzled by what I'm seeing.

 

I need the HLS-generated block to be started solely by the PL (no intervention whatsoever from the PS). I've created a simple counter which, when it overflows, sends a one-clock pulse to the "start" port of the ap_ctrl interface responsible for starting the HLS-generated IP block. As soon as it does so, the "idle" signal goes low, but immediately returns to the high position as soon as the "start" signal is de-asserted (see picture below).

waveform.png

 

If I hold the "start" port high indefinitely, all of the block's other ap_ctrl ports remain stuck at zero.

 

The very first task that my block should perform is to initialize a 72-bit bus, which is the "tdata" field for the DataMover command interface. Interestingly, this bus seems to never change in value, which leads me to believe that my IP block is never actually starting!

 

Does anyone more experienced know why this behavior would occur?

0 Kudos
6 Replies
Teacher muzaffer
Teacher
6,474 Views
Registered: ‎03-31-2012

Re: HLS-generated IP doesn't start

Normally you pulse start which causes idle to go low. After processing is done, idle should go high and done should go high too. So the major issue is done is not going high in your case. Also I am not sure how idle can go low at the same cycle as start. I thought it would be registered.
What does your ready signal look like?

Also did you simulate your counter block & HLS IP RTL together? It's much easier to see what's going on in simulation than in hardware.
- Please mark the Answer as "Accept as solution" if information provided is helpful.
Give Kudos to a post which you think is helpful and reply oriented.
0 Kudos
Highlighted
Scholar u4223374
Scholar
6,464 Views
Registered: ‎04-26-2015

Re: HLS-generated IP doesn't start

I've seen this before; unfortunately the conclusion (after much debugging) was essentially "it's all screwed up". As far as I can tell, HLS made a mess of the state machine, resulting in it finding some odd states that shouldn't really be possible (eg. ap_start, ap_done, and ap_ready all continuously asserted).

 

It hasn't happened recently, but I'm not sure whether that was because I've gotten better at coding for HLS or because newer versions of HLS avoid this problem. In your case, upgrading to the latest version of HLS would definitely be worth a try, if you're not already using it.

 

 

0 Kudos
Visitor agb3
Visitor
6,455 Views
Registered: ‎07-01-2016

Re: HLS-generated IP doesn't start

The ready signal always remains low.

 

I actually haven't simulated the two in conjunction. The counter is trivial enough that I didn't consider it worth the effort. But, given that I'm running into problems, it would probably be worthwhile to do so.

0 Kudos
Visitor agb3
Visitor
6,454 Views
Registered: ‎07-01-2016

Re: HLS-generated IP doesn't start

@u4223374:
I'm on the newest version of HLS. Is there some sort of coding style guide? I've found that many seemingly benign behaviors seem to cause the strangest strangest results with HLS...

0 Kudos
Scholar u4223374
Scholar
6,436 Views
Registered: ‎04-26-2015

Re: HLS-generated IP doesn't start

@agb3

 

Not that I've found. Would you mind sharing the code? I can at least have a look over it and see if there's anything obvious.

0 Kudos
Visitor agb3
Visitor
6,403 Views
Registered: ‎07-01-2016

Re: HLS-generated IP doesn't start

@u4223374: Of course. Here it is. Thank you very much for taking the time to examine it.

 

The basic idea is that my IP block should control an AXI DataMover to poke an address in uncached memory. A high-level overview of what I'm trying to do here:

 

(1) Issuing a read command to the DataMover FIFO

(2) Poking one byte of the stream read back from the DM

(3) Sending the a write command to the DM FIFO

(4) Streaming back a buffer with all the previous data and the poked byte.

 

#include <stdio.h>
#include <string.h>
#include <stdint.h>

#include <ap_int.h>
#include <ap_utils.h>

// Does the datamover have xuser and xcache fields enabled?
// Change to a define if so.
#undef XUSER_XCACHE

// Parameters
#define BROWSE_ADDRESS 0x11111110
#define XFER_SIZE 128
#define AXI_WIDTH 32

// Control register description
#define NBITS_BTT    23
#define NBITS_TYPE   1
#define NBITS_DSA    6
#define NBITS_EOF    1
#define NBITS_DRR    1
#define NBITS_SADDR  32
#define NBITS_TAG    4
#define NBITS_XUSER  4
#define NBITS_XCACHE 4

template<int w>
struct axi_stream {
	ap_uint<w>   data;
	ap_uint<w/8> keep;
	ap_uint<1>   last;
};

// Control stream tdata bus
struct ctrl_tdata {
	ap_uint<NBITS_BTT>    btt;
	ap_uint<NBITS_TYPE>   type;
	ap_uint<NBITS_DSA>    dsa;
	ap_uint<NBITS_EOF>    eof;
	ap_uint<NBITS_DRR>    drr;
	ap_uint<NBITS_SADDR>  saddr;
	ap_uint<NBITS_TAG>    tag;
	ap_uint<4>  RESERVED;
	// xUSER and xCACHE are only present if specifically requested by the DataMover IP.
	#ifdef XUSER_XCACHE
	ap_uint<NBITS_XUSER>  xuser;
	ap_uint<NBITS_XCACHE> xcache;
	#endif
} __attribute__((packed));
typedef struct ctrl_tdata ctrl_tdata;

struct ctrl_stream {
	ap_uint<1> valid;
	ap_uint<1> ready;
	struct ctrl_tdata tdata;
};

// Control register helpers
void set_eof(struct ctrl_stream *cs) {
	(cs->tdata).eof = true;
}

void clear_eof(struct ctrl_stream *cs) {
	(cs->tdata).eof = false;
}

void set_xfer_size(struct ctrl_stream *cs, int sz) {
	(cs->tdata).btt = (ap_uint<23>) sz;
}

void set_address(struct ctrl_stream *cs, uint32_t addr) {
	(cs->tdata).saddr = addr;
}

// Pulse the valid signal on the control stream to push a
// command onto the command FIFO.
void push_command(ctrl_stream *cs) {
	cs->valid = false;
	ap_wait();
	// XXX: Should we wait until ready?
	//while (!cs->ready)
	//	ap_wait();
	cs->valid = true;
	ap_wait();
	cs->valid = false;
}

// Copy from AXI port into buffer, setting AXI sidechannel signals appropriately.
// Also "blinks" the valid signal on ctrl_stream, pushing a command into the FIFO.
// Assumes that a valid and suitable command has already been written to the
// control stream!
template <int axi_w, int buf_w>
static void axis_read(struct axi_stream<axi_w> *axi,
		              struct ctrl_stream *cs,
		              ap_uint<buf_w> * buf, int len) {

	int i;
	int new_len;
	int final;
	ap_uint<axi_w> *buf_realigned = (ap_uint<axi_w> *) buf;
	new_len = len * buf_w / axi_w;
	final   = new_len - 1;

	// Push the command onto the command FIFO.
	clear_eof(cs);
	push_command(cs);

	for (i = 0; i < new_len; i++) {
		if (axi->keep) {
			buf_realigned[i] = axi->data[i];
		}
		if (i == final)
			set_eof(cs);
		ap_wait();
	}
}


// Copy from AXI port into buffer, setting AXI sidechannels appropriately.
// Also "blinks" ctrl_stream's validity so that a command is accepted to the
// control FIFO.
// Assumes that a valid and suitable command has already been written to the
// control stream!
template <int axi_w, int buf_w>
static void axis_write(struct axi_stream<axi_w> *axi,
		              struct ctrl_stream *cs,
		              ap_uint<buf_w> *buf,
					  int len) {
	int i;
	int final;
	int new_len;
	ap_uint<axi_w> *buf_realigned = (ap_uint<axi_w> *) buf;
	new_len = (len * buf_w / axi_w);
	final   = new_len - 1;

	clear_eof(cs);
	push_command(cs);

	for (i = 0; i < new_len; i++)
	{
		// NB: Assume that we keep all bytes -- so the axi_w evenly divides
		//     the total buffer bit-size.
		axi->keep = ~0;
		if (i == final)
			axi->last = true;
			set_eof(cs);
		axi->data = buf_realigned[i];
		ap_wait();
	}
}

// Zero the ctrl_tdata struct.
void _init_ctrl_stream(struct ctrl_stream *cs){
	cs->valid = false;

	(cs->tdata).btt  = 0;
	(cs->tdata).type = 1;
	(cs->tdata).dsa  = 0;
	(cs->tdata).eof  = 0;
	(cs->tdata).drr  = 0;
	(cs->tdata).saddr= 0;
	(cs->tdata).tag  = 0;
	// At some point in the future, it may become unwise to write to a
	// "reserved" port. However, we need some way of hinting to HLS that this
	// entire represents an output, not an input. So, we have to write to it.
	(cs->tdata).RESERVED = 0;
	#ifdef XUSER_XCACHE
	(cs->tdata).xuser  = 0;
	(cs->tdata).xcache = 0;
	#endif
}


///////////////////////// TOP-LEVEL FUNCTION //////////////////////

void ddr_diver(struct axi_stream<AXI_WIDTH> *axi_s2mm,
			   struct axi_stream<AXI_WIDTH> *axi_mm2s,
			   struct ctrl_stream *s2mm_ctrl,
			   struct ctrl_stream *mm2s_ctrl) {

	#pragma HLS INTERFACE ap_none port=s2mm_ctrl->valid
	#pragma HLS INTERFACE ap_none port=mm2s_ctrl->valid
	#pragma HLS INTERFACE ap_none port=s2mm_ctrl->tdata
	#pragma HLS INTERFACE ap_none port=mm2s_ctrl->tdata

	#pragma HLS INTERFACE ap_none port=axi_s2mm->keep
	#pragma HLS INTERFACE ap_none port=axi_s2mm->last

	#pragma HLS INTERFACE ap_none port=axi_mm2s->keep
	#pragma HLS INTERFACE ap_none port=axi_mm2s->last

	#pragma HLS INTERFACE axis port=axi_s2mm->data
	#pragma HLS INTERFACE axis port=axi_mm2s->data
	#pragma HLS DATA_PACK variable=s2mm_ctrl->tdata
	#pragma HLS DATA_PACK variable=mm2s_ctrl->tdata

	static ap_uint<32> ddr_buf[XFER_SIZE];

	// Is it stupid to set these every time we run this?
	// Yes, absolutely -- but it's the only way that we can tell HLS that they
	// need to be rendered as outputs. Having unused inputs will make the IP
	// hang before starting as it expects to have all inputs satisfied before
	// proceeding.
	_init_ctrl_stream(s2mm_ctrl);
	_init_ctrl_stream(mm2s_ctrl);

	set_xfer_size(s2mm_ctrl, XFER_SIZE);
	set_address(s2mm_ctrl, BROWSE_ADDRESS);
	set_xfer_size(mm2s_ctrl, XFER_SIZE);
	set_address(mm2s_ctrl, BROWSE_ADDRESS);

	axis_read(axi_mm2s, mm2s_ctrl, ddr_buf, XFER_SIZE);
	ddr_buf[0] = '\x00'; // Poke one value.
	axis_write(axi_s2mm, s2mm_ctrl, ddr_buf, XFER_SIZE);
}
0 Kudos