0
\$\begingroup\$

Searching "hls axi dma" on this site gives a few related issues but none of which I can use to fully deduct a solution for my problem.

My code generates "random" output on IO1 and reads input on IO4. To test my code, I short IO1 and IO4 and, I keep reading IO4, and whenever there is a signal in, I read what was on the output IO1. Ideally whenever IO4 has signal in, IO1 should also read signal out. I tested it this way to see whether I was off by one and needed adjustements to code or if everything was working as intended.

This is my block design

enter image description here

And here is the code

typedef ap_axis<8,1,1,1> stream_type;
static ap_uint<32> lfsr = 51763;


ap_uint<4> ioctrl(bool run, hls::stream<stream_type> axis_in, hls::stream<stream_type> axis_out, ap_uint<14> &usr_gpio, volatile ap_uint<12> gpio_i, volatile ap_uint<2> gpio_o) {
#pragma HLS INTERFACE ap_ctrl_none port=return
#pragma HLS INTERFACE s_axilite port=run
#pragma HLS INTERFACE s_axilite port=usr_gpio
#pragma HLS INTERFACE axis port=axis_in
#pragma HLS INTERFACE axis port=axis_out
#pragma HLS INTERFACE ap_none port=gpio_o
#pragma HLS INTERFACE ap_none port=gpio_i

    ap_uint<4> state = 0;


    if (run) {
        usr_gpio = (ap_uint<14>) gpio_i + ((ap_uint<14>) gpio_o << 12); // This was used for reading before AXI implementation

        bool b_32 = (lfsr & (1<<31)) >> 31;
        bool b_22 = lfsr & (1<<21) >> 21;
        bool b_2 = (lfsr & (1<<1)) >> 1;
        bool b_1 = (lfsr & (1<<0));

        bool new_bit = b_32 ^ b_22 ^ b_2 ^ b_1;
        lfsr = lfsr >> 1;
        lfsr = lfsr | (new_bit << 31);

        state = (ap_uint<4>) (lfsr.to_uint() & 1);

        if (state == 1) {
            state = 5; // 0101 = [IO 0, LED 0]
        } else if (state == 0) {
            state = 10; // 1010 = [IO 1, LED 1]
        }

        // AXI
        stream_type val_in = axis_in.read();
        stream_type val_out;

        // Here I check whether gpio input is detected
        ap_uint<14> bit_hit = (ap_uint<14>) gpio_i & (ap_uint<14>) 0x02;
        if (bit_hit == 0) {
            // I read gpio output and generate a val_out.data (should ideally always be 0x3)
            ap_uint<8> out_bit = (ap_uint<8>) gpio_o & (ap_uint<8>) 0x01;
            val_out.data = (1<<1) | out_bit;
        } else {
            // If nothing detected, set 0
            val_out.data = 0;
        }

        // As a slave to tutorials, I just copy over everything I don't understand.
        val_out.keep = val_in.keep;
        val_out.strb = val_in.strb;
        val_out.user = val_in.user;
        val_out.last = val_in.last;
        val_out.id = val_in.id;
        val_out.dest = val_in.dest;

        axis_out.write(val_out);

    } else {
        usr_gpio = 0;
    }

    // Set output
    return state;

}

Now, I allocate streams on PS, send and empty allocated stream to PL, which will initiate the output stream from PL, and I read output buffer on PS. These are some of values of the AXI stream I read from PS: [0, 0, 0, 2, 2, 0, 3, 2, 2, 0, 0, 3, ...]

The 0's are fine I though, and the 3's. But what the hell are the 2's. 2 = b'10', that means I hit this line

val_out.data = (1<<1) | out_bit;

But how can gpio input be detected without anything being outputted. Furthermore, bitstream is generated at 100MHz clock. If setting the clock to 250MHz (in PS from pynq.clocks), there are more 3's in the stream to PS, while setting lower frequencies.. say 25MHz, all 3's are gone and only 0's and 2's are read.

Theories:

  • I have misunderstand FPGA's chronological execution and this alignment is completely random!?
  • I should not set TLAST on every cycle, only when stream in buffer is finished. !?

Appendix :slight_smile: This my AXI DMA setup

enter image description here

\$\endgroup\$

2 Answers 2

1
\$\begingroup\$

First, changing the clock frequency to something the design wasn't synthesized for, might produce wrong results (having negative slack and so on).

To fully answer your question, there are a few things that require some clarification from your side:

  • First: what do you mean by sending an empty buffer? Your IP expects to read a packet for each one produced (since the fifo read() method is blocking).
  • Second: what's the code you're running on the PS? It's unlucky, but you might setup the DMA wrongly (and forwarding the side channels, like TLAST, on the stream is correct here).

Regarding your IP code instead, the ports that are in the feed-back loop have an ap_none interface type, so I believe VIVADO HLS doesn't put any register there. Thus, you might be reading something not clocked. There's an option on the interface to add a register to the port (something like: -register), that might be a starting point.

On a side note, I would avoid using bool types (so switch to ap_uint<1>) and try the PIPELINE directive. Finally, always check that the simulation and cosimulation pass (even though the feedback connection might be tricky to do, since HLS behaves strangely with feedback loops).

\$\endgroup\$
0
\$\begingroup\$

Do you know how to use ILA? I suggest this to see what the AXI-S connections are doing if you're working on the live chip.

Also, have you constrained your design correctly? Seems like you're working with several clock domains here.

\$\endgroup\$
1
  • \$\begingroup\$ No, I'm a noob, don't even know what ILA is. I thought everything is clocked through FCLK_CLK0 from ZYNQ7 Processing System. What other clock had you in mind? \$\endgroup\$
    – niCk cAMel
    Commented Feb 18, 2021 at 10:57

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.