libiio icon indicating copy to clipboard operation
libiio copied to clipboard

Add new iio_block based API and IIOD async. protocol

Open pcercuei opened this issue 2 years ago • 68 comments

This PR contains my latest work on the new Libiio 1.x API.

The API changed quite a bit, to be more versatile, to map better to the latest IIO kernel APIs, and to enable more performance. Along with the API change, the communication protocol between libiio and IIOD changed; it will now optionally use an asynchronous protocol (if supported by both parties) to boost the throughput.

For instance, running iio_readdev (iio_rwdev on the "dev" branch) with blocks of 16 KiB each, over a direct network cable between my PC and a ZCU102 equipped with a FMCOMMS-3: new libiio + new iiod: 81 MiB/s sustained Any other configuration: 44 MiB/s sustained

The diff is huge so take your time to review.

pcercuei avatar May 16 '22 13:05 pcercuei

@catkira It depends what modifications you need. If your timestamps appear as a separate streamable channel, then it should work with latest master without changes.

pcercuei avatar Jun 27 '22 11:06 pcercuei

@catkira probably not a good idea to base your branch off this one, because I'm not done working on it and I will force-push some changes from time to time. Maybe a better idea would be to base your branch off the "dev" branch instead, which is where all new features are merged, and will never be force-pushed to. Just keep in mind that the API in the "dev" branch won't be stable until it's eventually merged into master.

pcercuei avatar Jun 27 '22 11:06 pcercuei

@catkira The "dev" branch is basically "libiio2" (which will actually be libiio 1.x, since libiio1 was 0.x). It will be merged into master right after we release libiio v0.24, which will most likely be the last one of the 0.x series. Then the development will continue in the master branch until we are happy enough with the code to make a libiio 1.x release.

I don't think we will change anything in libiio 1.x regarding timestamps. They will simply appear as an extra channel, that you can enable (or not).

pcercuei avatar Jun 27 '22 12:06 pcercuei

@catkira Right now what's implemented and works, is timestamp reporting at the sample level, if your HDL and kernel driver support that. Then the timestamp values appear as a separate channel.

What @tfcollins talks about in the link you pasted is supporting timestamps at the buffer level, where there is one timestamp value for a whole block of samples, which represents (for instance) the time at which the very first sample was read.

This kind of meta-data is not supported yet, and it is unclear if we want to add ways to support it. If we have meta-data embedded into the samples stream, then we can still transport it all the way to the application, which then must know that the first few bytes of each buffer is the timestamp, maybe with an extra kernel attribute that specifies that there is a timestamp for this buffer. This shifts the responsability of handling the timestamp to the application, which isn't ideal.

If we don't want to taint the samples stream with meta-data, then maybe the kernel could also somehow update a "timestamp" buffer attribute each time a block of samples is dequeued. This could be handled by libiio as a special attribute, and you'd get something like a iio_buffer_get_timestamp() call to retrieve it. But then, that means your HDL must write the timestamp value outside the samples buffer, and the kernel driver must retrieve the value to update the "timestamp" attribute.

We tend to see this problem as part of a bigger one: how should meta-data (in general) generated by the HDL or hardware be handled by the kernel, libiio, and the application? We don't really have an answer for that at the moment.

pcercuei avatar Jun 27 '22 15:06 pcercuei

@catkira so I didn't profile it yet to see exactly what is the new bottleneck, but I'm pretty sure it's the CPU. The data is copied by the CPU from the IIO buffer to the network socket yes, and it's the only time it's copied. At 81 MiB/s the CPU usage was very high.

pcercuei avatar Jun 28 '22 11:06 pcercuei

@catkira well I'm not testing on a Pluto ;) But on a ZCU706 with a FMCOMMS-3 board.

pcercuei avatar Jul 02 '22 09:07 pcercuei

@catkira well I'm not testing on a Pluto ;) But on a ZCU706 with a FMCOMMS-3 board.

Ah that makes sense. My AntSDR should arrive soon, then I can test over its GBit ethnernet interface. Can You share Your benchmark script? Or do You just stream to file for a while and then divide size/duration?

catkira avatar Jul 02 '22 09:07 catkira

@catkira I just use the --benchmark option of iio_rwdev.

pcercuei avatar Jul 02 '22 09:07 pcercuei

The iio_buffer now represents a hardware IIO buffer. Generally you can create only one per device, but some IIO devices can have more than one. The iio_buffer has enable/disable functions to start/stop the DMA operation. Your application is now responsible for creating a few iio_blocks, which are dequeued by default, and can be enqueued to upload samples, or to request samples from the hardware. A iio_block basically just represents a block of samples.

The iio_stream object is something else, it is implemented on top of the iio_buffer and iio_block objects. It is totally optional, and is only there to simplify the process for simple programs that don't want/need to manually allocate the blocks. You just create the iio_stream object, and call iio_stream_get_next_block() in a loop - the old block will be enqueued, and it will return a pointer to a newly dequeued block.

pcercuei avatar Jul 02 '22 17:07 pcercuei

I think we should discuss the timestamp feature somewhere else, feel free to open an issue about it.

pcercuei avatar Jul 04 '22 10:07 pcercuei

I think we should discuss the timestamp feature somewhere else, feel free to open an issue about it.

yes, that makes sense. Its a bit too much spam here. I delete my posts here and open another issue when I have a working prototype.

catkira avatar Jul 04 '22 13:07 catkira

@catkira you can post them to another issue now, then we can discuss it there. I can give some feedback but didn't want to pollute this PR further :)

pcercuei avatar Jul 04 '22 13:07 pcercuei

I did a little benchmarking with ANTSDR (A pluto clone with gigabit ethernet and slightly larger FPGA). I can get about 12-14 MSPS single channel (tested with SigDigger and SoapyPlutoSDR). While I was doing that, 'top' on ANTSDR showed 50% CPU usage. Since the 50% is an average value, I guess there a phases with 100% too, so I guess CPU power is the limiting factor here. The reason why this libiio is faster, is probably because it is doing stuff more efficiently. I would like to try out this branch to see how many MSPS I can get. @pcercuei Is there a way You recommend how to try out this branch? Like patch buildroot and create a new image, or just scp the new libiio onto the SDR after bootup? Does this libiio branch also need a new kernel or updated drivers? For the PC its easy, I can just compile this branch I guess.

catkira avatar Jul 10 '22 18:07 catkira

@catkira I believe for testing you can just SCP the updated libiio + IIOD, yes; then kill the running instance of iiod and run your own. But since it's a bit cumbersome to run it manually every time you restart the device, updating the buildroot package is a good idea as well.

The updated libiio does not need new kernel drivers.

pcercuei avatar Jul 11 '22 13:07 pcercuei

What @tfcollins talks about in the link you pasted is supporting timestamps at the buffer level, where there is one timestamp value for a whole block of samples, which represents (for instance) the time at which the very first sample was read.

This kind of meta-data is not supported yet, and it is unclear if we want to add ways to support it. I

I think we absolutely need to support this type of support, There are too many other systems that build on top of IIO (or could) that require things like this. How are TDD systems managed otherwise? (Send this buffer at sample count=X is just as important as the Rx sample counting).

rgetz avatar Jul 19 '22 14:07 rgetz

@rgetz yes, what I meant in my message was that it's unclear if we want to add support for this in libiio + kernel IIO, instead of just treating the metadata as sample data and let the IP blocks decypher. The major fear I have with supporting it in IIO is to come up with a system that's too "rigid" to support anything else that might come up in the future...

pcercuei avatar Jul 19 '22 14:07 pcercuei

should we continue to discuss this in the timestamp issue?

Treating meta data as samples in libiio and kernel iio+dmac would mean that the user code would have to know how the hdl core expects meta data and that meta data would then be mandatory even if the user code just wants to do streaming. Therefore I think from a user perspective, it would be nicer to have a dedicated timestamp API. But that could also be done as another layer on top of (or inside) libiio so that all the timestamp stuff is transparent for libiio and kernel iio+dmac.

catkira avatar Jul 19 '22 15:07 catkira

Yeah, I understand @pcercuei 'S concern - we don't know what we don't know yet. But I think there are lots of open implementations that would use/need/do similar things. We should be able to learn from others systems.

Thinking:

  • Vita 49
  • UHD
  • open LTE stacks
  • others

Most of those are on the high speed / SDR; but I think there are similar sample stamping needs on IMUs, and precision side too.

Some (high speed interface) may need to have some sort of hardware assist (HDL), where the standard SPI like interfaces might be able to get by with a pure SW implementation.

  • Robin

rgetz avatar Jul 21 '22 20:07 rgetz

When compiling, zlibstd is not found. I have written the details in this issue https://github.com/analogdevicesinc/libiio/issues/867#issuecomment-1204220359

Edit: its fixed now, I had to add zstd as dependency to the libiio package in buildroot -> https://github.com/catkira/buildroot/commit/d1c0130beef58096ff00343cfce5c8d4a6b228ec

catkira avatar Aug 03 '22 17:08 catkira

@pcercuei which linux (repo and branch) should I use to try out the new dmabuf und mmap stuff of this PR?

catkira avatar Aug 04 '22 11:08 catkira

@catkira It's currently vacation season, so replies might be delayed.

mhennerich avatar Aug 04 '22 12:08 mhennerich

I have tried to scp the cross compiled libiio to pluto. But when I start iiod I get an error message:

# /etc/init.d/S23udc start
Starting UDC Gadgets: sh: write error: No such device
FAIL

I have copied the files using this script:

#~/bin/sh
# This script copies libiio and iiod to pluto and restarts iiod
#

#default IP address
ipaddr=192.168.2.1
if [ "$#" -eq 1 ]; then
	ipaddr="$1"
fi
echo "using ip address $ipaddr"

if [ ! -f ./build/libiio.so ]; then
    echo no file to upload
    exit
fi

ssh_cmd()
{
    sshpass -v -panalog ssh -oStrictHostKeyChecking=no -oUserKnownHostsFile=/dev/null -oCheckHostIP=no root@${ipaddr} "$1" 2>/dev/null &
    wait
    if [ "$?" -ne "0" ]; then
        echo ssh command \"$1\" failed
        exit
    fi
}

scp_cmd()
{
    sshpass -v -panalog scp -oStrictHostKeyChecking=no -oUserKnownHostsFile=/dev/null -oCheckHostIP=no "$1" root@${ipaddr}:"$2" 2>/dev/null &
    wait
    if [ "$?" -ne "0" ]; then
        echo scp command copy $1 tp $2 failed
        exit
    fi
}

ssh_cmd "/etc/init.d/S23udc stop"

ssh_cmd "mkdir /root/libiio_orig"

echo "copy libiio.so"
ssh_cmd "mv /usr/lib/libiio* /root/libiio_orig"
scp_cmd "./build/libiio.so" "/usr/lib/"

echo "copy iiod"
ssh_cmd "mv /usr/sbin/iiod /root/libiio_orig"
scp_cmd "./build/iiod/iiod" "/usr/sbin/"

ssh_cmd "/etc/init.d/S23udc start"

on the build system libiio.so is a symlink to libiio.so.1 which is a symlink to libiio.so.1.0. But when I scp libiio.so it automatically follows the symlink and copies libiio.so.1.0. Thats why I only copy one lib. It should be ok right? @pcercuei Do I need to update more files? maybe some config files?

catkira avatar Aug 04 '22 19:08 catkira

Ah i see the problem, libiio.so.1 is imported by iiod

benja@DESKTOP:/mnt/d/git/libiio_cross/build/iiod$ readelf -d iiod

Dynamic section at offset 0xfc04 contains 35 entries:
  Tag        Type                         Name/Value
 0x00000001 (NEEDED)                     Shared library: [libiio.so.1]

catkira avatar Aug 05 '22 10:08 catkira

I changed the script to look like this

#~/bin/sh
# This script copies libiio and iiod to pluto and restarts iiod
#

#default IP address
ipaddr=192.168.2.1
if [ "$#" -eq 1 ]; then
	ipaddr="$1"
fi
echo "using ip address $ipaddr"

if [ ! -f ./build/libiio.so ]; then
    echo no file to upload
    exit
fi

ssh_cmd()
{
    sshpass -v -panalog ssh -oStrictHostKeyChecking=no -oUserKnownHostsFile=/dev/null -oCheckHostIP=no root@${ipaddr} "$1" 2>/dev/null &
    wait
    if [ "$?" -ne "0" ]; then
        echo ssh command \"$1\" failed
        exit
    fi
}

scp_cmd()
{
    sshpass -v -panalog scp -oStrictHostKeyChecking=no -oUserKnownHostsFile=/dev/null -oCheckHostIP=no "$1" root@${ipaddr}:"$2" 2>/dev/null &
    wait
    if [ "$?" -ne "0" ]; then
        echo scp command copy $1 tp $2 failed
        exit
    fi
}

ssh_cmd "/etc/init.d/S23udc stop"

ssh_cmd "mkdir /root/libiio_orig"

cd build && tar -cvf libiio.tar libiio.so* && cd ..
echo "copy libiio.tar"
ssh_cmd "mv /usr/lib/libiio* /root/libiio_orig"
scp_cmd "build/libiio.tar" "/usr/lib/"
ssh_cmd "tar -xvf /usr/lib/libiio.tar -C /usr/lib"

echo "copy iiod"
ssh_cmd "mv /usr/sbin/iiod /root/libiio_orig"
scp_cmd "./build/iiod/iiod" "/usr/sbin/"

ssh_cmd "/etc/init.d/S23udc start"

but the problem is also that libc is not compatible, because I cross compiled libiio with vitis 2022.1 :/ Seems like I have to create a complete new image.

catkira avatar Aug 05 '22 10:08 catkira

@pcercuei When I try to compile SoapyPlutoSDR with this new libiio I get some errors (I already included iio-compat.h in SoapyPlutoSDR and I also added iio-compat.h to the list of installed headers in CMakefiles.txt, see this commit https://github.com/catkira/libiio/commit/83a5f5d3c491f67b1a76bbcd49ce4e882fdcedaa)

benja@DESKTOP:/mnt/d/git/SoapyPlutoSDR/build$ make
Consolidate compiler generated dependencies of target PlutoSDRSupport
make[2]: Warning: File 'CMakeFiles/PlutoSDRSupport.dir/compiler_depend.make' has modification time 0.23 s in the future
[ 20%] Building CXX object CMakeFiles/PlutoSDRSupport.dir/PlutoSDR_Registration.cpp.o
[ 40%] Building CXX object CMakeFiles/PlutoSDRSupport.dir/PlutoSDR_Settings.cpp.o
/mnt/d/git/SoapyPlutoSDR/PlutoSDR_Settings.cpp:157:59: error: macro "iio_channel_attr_read" passed 4 arguments, but takes just 3
  157 |   if (iio_channel_attr_read(chn, "input", buf, sizeof(buf)) > 0) {
      |                                                           ^
In file included from /usr/include/iio/iio-compat.h:16,
                 from /mnt/d/git/SoapyPlutoSDR/SoapyPlutoSDR.hpp:2,
                 from /mnt/d/git/SoapyPlutoSDR/PlutoSDR_Settings.cpp:1:
/usr/include/iio/iio.h:1029: note: macro "iio_channel_attr_read" defined here

I think the problem is this part

__api __check_ret ssize_t
iio_channel_attr_read_raw(const struct iio_channel *chn,
			  const char *attr, char *dst, size_t len);


/** @brief Read the content of the given channel-specific attribute
 * @param chn A pointer to an iio_channel structure
 * @param attr A NULL-terminated string corresponding to the name of the
 * attribute
 * @param ptr A pointer to the variable where the value should be stored
 * @return On success, 0 is returned
 * @return On error, a negative errno code is returned */
#define iio_channel_attr_read(dev, attr, ptr)			\
	_Generic((ptr),						\
		 bool *: iio_channel_attr_read_bool,		\
		 long long *: iio_channel_attr_read_longlong,	\
		 double *: iio_channel_attr_read_double)(dev, attr, ptr)

with this define it is not possible to add a iio_channel_attr_read function with 4 arguments for compatibility. It is probably better not to use define like this in a header file. I think the comment for the function is also not correct, because 1st argument is not a channel.

catkira avatar Aug 06 '22 19:08 catkira

When I try ad9361-iiostream, I get this error:

# ./ad9361-iiostream
* Acquiring IIO context
* Acquiring AD9361 streaming devices
* Configuring AD9361 for streaming
* Acquiring AD9361 phy channel 0
* Acquiring AD9361 RX lo channel
* Acquiring AD9361 phy channel 0
* Acquiring AD9361 TX lo channel
* Initializing AD9361 IIO streaming channels
* Enabling IIO streaming channels
* Creating non-cyclic IIO buffers with 1 MiS
* Starting IO streaming (press CTRL+C to cancel)
Segmentation fault

Its on a pluto clone (ant sdr). I had to outcomment a line in ad9361-iiostream., because of this error:

# ./ad9361-iiostream
* Acquiring IIO context
* Acquiring AD9361 streaming devices
* Configuring AD9361 for streaming
* Acquiring AD9361 phy channel 0
Error -22 writing to channel "rf_port_select"
value may not be supported.
* Destroying streams
* Destroying buffers
* Destroying channel masks
* Destroying context
# unloading
bool cfg_ad9361_streaming_ch(struct stream_cfg *cfg, enum iodev type, int chid)
{
        struct iio_channel *chn = NULL;

        // Configure phy and lo channels
        printf("* Acquiring AD9361 phy channel %d\n", chid);
        if (!get_phy_chan(type, chid, &chn)) {  return false; }
        //wr_ch_str(chn, "rf_port_select",     cfg->rfport);
        wr_ch_lli(chn, "rf_bandwidth",       cfg->bw_hz);
        wr_ch_lli(chn, "sampling_frequency", cfg->fs_hz);

        // Configure LO channel
        printf("* Acquiring AD9361 %s lo channel\n", type == TX ? "TX" : "RX");
        if (!get_lo_chan(type, &chn)) { return false; }
        wr_ch_lli(chn, "frequency", cfg->lo_hz);
        return true;
}

catkira avatar Aug 07 '22 20:08 catkira

@pcercuei there were many more things missing in iio-compat.h, so I gave up on that ported SoapyPlutoSDR to libiio 1.0.

Is it planned that iio-compat.h provides full 0.x compatibility?

catkira avatar Aug 08 '22 08:08 catkira

@catkira the DMABUF kernel code can be found here: https://lore.kernel.org/linux-doc/[email protected]/T/

Your SoapyPlutoSDR needs to be ported to Libiio 1.0, you can't expect it to compile out of the box. The compat library is lagging behind and needs a good overhaul. Some functions changed (e.g. iio_device_create_buffer) so it's not really possible to just link the compat lib with libiio 1.x, instead it will need to dynamically load the symbols (dlopen / dlsym), and that's TODO.

About your ad9361-iiostream... Well, the Pluto does not have a AD9361 ;)

pcercuei avatar Aug 08 '22 09:08 pcercuei

@pcercuei I already ported SoapyPlutoSDR to libiio 1.0. (At least the rx stuff). It seems to kind of work, but I have to further evaluate it.

https://github.com/catkira/SoapyPlutoSDR/tree/libiio_1.0

One thing I was wondering about was, if there is a way to get the lib and backend version in libiio 1.0? In 0.x there was iio_context_get_version and iio_library_get_version, in 1.0 I only found iio_context_get_version_minor.

I have modified my pluto to have a AD9361 by chaning some variables with fw_setenv. My pluto clone, the antsdr, has a real ad9361. So both should appear to libiio as a AD9361. (all AD936x have the same silicon AFAIK and are just possibly selected by performance)

catkira avatar Aug 08 '22 09:08 catkira

@catkira lib and backend versions can be obtained with iio_context_get_version_*, to get the library version, just pass ctx=NULL.

pcercuei avatar Aug 08 '22 09:08 pcercuei