Search code examples
c++fpgaxilinxgnuradiognuradio-companion

Monitor buffers in GNU Radio


I have a question regarding buffering in between blocks in GNU Radio. I know that each block in GNU (including custom blocks) have buffers to store items that are going to be sent or received items. In my project, there is a certain sequence I have to maintain to synchronize events between blocks. I am using GNU radio on the Xilinx ZC706 FPGA platform with the FMCOMMS5.

In the GNU radio companion I created a custom block that controls a GPIO Output port on the board. In addition, I have an independent source block that is feeding information into the FMCOMMS GNU block. The sequence I am trying to maintain is that, in GNU radio, I first send data to the FMCOMMS block, second I want to make sure that the data got consumed by the FMCOMMS block (essentially by checking buffer), then finally I want to control the GPIO output.

From my observations, the source block buffer doesn’t seem to send the items until it’s full. This will cause a major issue in my project because this means that the GPIO data will be sent before or in parallel with sending the items to the other GNU blocks. That’s because I’m setting the GPIO value through direct access to its address in the ‘work’ function of my custom block.

I tried to use pc_output_buffers_full() in the ‘work’ function of my custom source in order to monitor the buffer, but I’m always getting 0.00. I’m not sure if it’s supposed to be used in custom blocks or if the ‘buffer’ in this case is something different from where the output items are stored. Here's a small code snippet which shows the problem:

char level_count = 0, level_val = 1;
vector<float> buff (1, 0.0000);
for(int i=0; i< noutput_items; i++)
{
    if(level_count < 20 && i< noutput_items)
    {
        out[i] = gr_complex((float)level_val,0);
        level_count++;
    }
    else if(i<noutput_items)
    {
        level_count = 0;
        level_val ^=1;
        out[i] = gr_complex((float)level_val,0);
    }
    buff = pc_output_buffers_full();
    for (int n = 0; n < buff.size(); n++)
        cout << fixed << setw(5) << setprecision(2) << setfill('0') << buff[n] << " ";
    cout << "\n";
}

Is there a way to monitor the buffer so that I can determine when my first part of data bits have been sent? Or is there a way to make sure that the each single output item is being sent like a continuous stream to the next block(s)?

GNU Radio Companion version: 3.7.8

OS: Linaro 14.04 image running on the FPGA


Solution

  • Or is there a way to make sure that the each single output item is being sent like a continuous stream to the next block(s)?

    Nope, that's not how GNU Radio works (at all!):

    A while back I wrote an article that explains how GNU Radio deals with buffers, and what these actually are. While the in-memory architecture of GNU Radio buffers might be of lesser interest to you, let me quickly summarize the dynamics of it:

    • The buffers that (general_)work functions are called with behave for all that's practical like linearly addressable ring buffers. You get a random number of samples at once (restrictable to minimum numbers, multiples of numbers), and all that you not consume will be handed to you the next time work is called.
    • These buffers hence keep track of how much you've consumed, and thus, how much free space is in a buffer.
    • The input buffer a block sees is actually the output buffer of the "upstream" block in the flow graph.
    • GNU Radio's computation is backpressure-controlled: Any block's work method will immediately be called in an endless loop given that:
      1. There's enough input for the block to do work,
      2. There's enough output buffer space to write to.
    • Therefore, as soon as one block finishes its work call, the upstream block is informed that there's new free output space, thus typically leading to it running
    • That leads to high parallelity, since even adjacent blocks can run simultaneously without conflicting
    • This architecture favors large chunks of input items, especially for blocks that take a relative long time to computer: while the block is still working, its input buffer is already being filled with chunks of samples; when it's finished, chances are it's immediately called again with all the available input buffer being already filled with new samples.
    • This architecture is asynchronous: even if two blocks are "parallel" in your flow graph, there's no defined temporal relation between the numbers of items they produce.

    I'm not even convinced switching GPIOs at times based on the speed computation in this completely non-deterministic timing data flow graph model is a good idea to start with. Maybe you'd rather want to calculate "timestamps" at which GPIOs should be switched, and send (timestamp, gpio state) command tuples to some entity in your FPGA that keeps absolute time? On the scale of radio propagation and high-rate signal processing, CPU timing is really inaccurate, and you should use the fact that you have an FPGA to actually implement deterministic timing, and use the software running on the CPU (i.e. GNU Radio) to determine when that should happen.

    Is there a way to monitor the buffer so that I can determine when my first part of data bits have been sent?

    Other than that, a method to asynchronously tell another another block that, yes, you've processed N samples, would be either to have a single block that just observes the outputs of both blocks that you want to synchronize and consumes an identical number of samples from both inputs, or to implement something using message passing. Again, my suspicion is that this is not a solution to your actual problem.