From 3af72991f65b9758a93be57f940dc17a5e3b6d73 Mon Sep 17 00:00:00 2001 From: Paul Cercueil Date: Tue, 5 Dec 2023 11:33:34 +0100 Subject: [PATCH 01/11] utils/iio-common: Use CLOCK_MONOTONIC clock for timeouts The CLOCK_REALTIME clock may jump in time, which can be a huge problem when computing timeouts. Use the CLOCK_MONOTONIC one instead, which does not have this problem. Signed-off-by: Paul Cercueil --- utils/iio_common.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/utils/iio_common.c b/utils/iio_common.c index bcaae5055..a108a0246 100644 --- a/utils/iio_common.c +++ b/utils/iio_common.c @@ -444,7 +444,7 @@ uint64_t get_time_us(void) #ifdef _MSC_BUILD timespec_get(&tp, TIME_UTC); #else - clock_gettime(CLOCK_REALTIME, &tp); + clock_gettime(CLOCK_MONOTONIC, &tp); #endif return tp.tv_sec * 1000000ull + tp.tv_nsec / 1000; From 2819ff36420c5d2b68d9afdb4f110e8594508b3c Mon Sep 17 00:00:00 2001 From: Paul Cercueil Date: Mon, 11 Dec 2023 10:29:08 +0100 Subject: [PATCH 02/11] iiod: Fix segmentation fault when buffer creation failed When the buffer creation failed, entry->buf was left to whatever error pointer it had previously, instead of being reset to NULL; cleanup code would subsequently try to destroy the buffer once again with an invalid pointer. Signed-off-by: Paul Cercueil --- iiod/ops.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/iiod/ops.c b/iiod/ops.c index 0019daf95..473078691 100644 --- a/iiod/ops.c +++ b/iiod/ops.c @@ -398,8 +398,8 @@ static int create_buf_and_blocks(struct DevEntry *entry, size_t samples_count, for (; i; i--) iio_block_destroy(entry->blocks[i - 1]); iio_buffer_destroy(entry->buf); - entry->buf = NULL; err_free_blocks_array: + entry->buf = NULL; free(entry->blocks); entry->blocks = NULL; return err; From 9466de621554ab18798245eeea35376db88669ed Mon Sep 17 00:00:00 2001 From: Paul Cercueil Date: Mon, 11 Dec 2023 11:39:43 +0100 Subject: [PATCH 03/11] Add "cyclic" parameter to .buffer_enable() backend callback IIOD v0.x supports enqueueing cyclic blocks, but it needs to know about it when the iio_buffer is opened through the "OPEN" command. As iiod-client calls this command when the buffer is to be enabled, pass an extra "cyclic" parameter to .buffer_enable(), always set to "false" for now. Signed-off-by: Paul Cercueil --- buffer.c | 3 ++- iiod-client.c | 4 ++-- include/iio/iio-backend.h | 2 +- include/iio/iiod-client.h | 2 +- local.c | 2 +- network.c | 4 ++-- serial.c | 4 ++-- usb.c | 4 ++-- 8 files changed, 13 insertions(+), 12 deletions(-) diff --git a/buffer.c b/buffer.c index eb759f8b6..e8b35afd7 100644 --- a/buffer.c +++ b/buffer.c @@ -46,6 +46,7 @@ static int iio_buffer_set_enabled(const struct iio_buffer *buf, bool enabled) { const struct iio_backend_ops *ops = buf->dev->ctx->ops; size_t sample_size, nb_samples = 0; + bool cyclic = false; if (buf->block_size) { sample_size = iio_device_get_sample_size(buf->dev, buf->mask); @@ -53,7 +54,7 @@ static int iio_buffer_set_enabled(const struct iio_buffer *buf, bool enabled) } if (ops->enable_buffer) - return ops->enable_buffer(buf->pdata, nb_samples, enabled); + return ops->enable_buffer(buf->pdata, nb_samples, enabled, cyclic); return -ENOSYS; } diff --git a/iiod-client.c b/iiod-client.c index 5039f0ace..92dbfde14 100644 --- a/iiod-client.c +++ b/iiod-client.c @@ -1490,7 +1490,7 @@ void iiod_client_free_buffer(struct iiod_client_buffer_pdata *pdata) } int iiod_client_enable_buffer(struct iiod_client_buffer_pdata *pdata, - size_t nb_samples, bool enable) + size_t nb_samples, bool enable, bool cyclic) { struct iiod_client *client = pdata->client; struct iiod_client_io *client_io; @@ -1511,7 +1511,7 @@ int iiod_client_enable_buffer(struct iiod_client_buffer_pdata *pdata, if (enable) { client_io = iiod_client_open_with_mask(client, pdata->dev, pdata->mask, - nb_samples, false); + nb_samples, cyclic); err = iio_err(client_io); pdata->io = err ? NULL : client_io; } else { diff --git a/include/iio/iio-backend.h b/include/iio/iio-backend.h index 73b5fec6c..ae907ba63 100644 --- a/include/iio/iio-backend.h +++ b/include/iio/iio-backend.h @@ -108,7 +108,7 @@ struct iio_backend_ops { struct iio_channels_mask *mask); void (*free_buffer)(struct iio_buffer_pdata *pdata); int (*enable_buffer)(struct iio_buffer_pdata *pdata, - size_t nb_samples, bool enable); + size_t nb_samples, bool enable, bool cyclic); void (*cancel_buffer)(struct iio_buffer_pdata *pdata); ssize_t (*readbuf)(struct iio_buffer_pdata *pdata, diff --git a/include/iio/iiod-client.h b/include/iio/iiod-client.h index 7582f7c60..1ec23f141 100644 --- a/include/iio/iiod-client.h +++ b/include/iio/iiod-client.h @@ -77,7 +77,7 @@ iiod_client_create_buffer(struct iiod_client *client, struct iio_channels_mask *mask); __api void iiod_client_free_buffer(struct iiod_client_buffer_pdata *pdata); __api int iiod_client_enable_buffer(struct iiod_client_buffer_pdata *pdata, - size_t nb_samples, bool enable); + size_t nb_samples, bool enable, bool cyclic); __api struct iio_block_pdata * iiod_client_create_block(struct iiod_client_buffer_pdata *pdata, diff --git a/local.c b/local.c index 89b337684..c3ee9731d 100644 --- a/local.c +++ b/local.c @@ -313,7 +313,7 @@ static int local_do_enable_buffer(struct iio_buffer_pdata *pdata, bool enable) } static int local_enable_buffer(struct iio_buffer_pdata *pdata, - size_t nb_samples, bool enable) + size_t nb_samples, bool enable, bool cyclic) { int ret; diff --git a/network.c b/network.c index 4e6dcb3d2..699307c79 100644 --- a/network.c +++ b/network.c @@ -485,9 +485,9 @@ void network_free_buffer(struct iio_buffer_pdata *pdata) } int network_enable_buffer(struct iio_buffer_pdata *pdata, - size_t block_size, bool enable) + size_t block_size, bool enable, bool cyclic) { - return iiod_client_enable_buffer(pdata->pdata, block_size, enable); + return iiod_client_enable_buffer(pdata->pdata, block_size, enable, cyclic); } struct iio_block_pdata * network_create_block(struct iio_buffer_pdata *pdata, diff --git a/serial.c b/serial.c index 390b05ae9..6116faae1 100644 --- a/serial.c +++ b/serial.c @@ -257,9 +257,9 @@ static void serial_free_buffer(struct iio_buffer_pdata *buf) } static int serial_enable_buffer(struct iio_buffer_pdata *buf, - size_t nb_samples, bool enable) + size_t nb_samples, bool enable, bool cyclic) { - return iiod_client_enable_buffer(buf->pdata, nb_samples, enable); + return iiod_client_enable_buffer(buf->pdata, nb_samples, enable, cyclic); } static ssize_t diff --git a/usb.c b/usb.c index d8e255909..40406324a 100644 --- a/usb.c +++ b/usb.c @@ -490,9 +490,9 @@ static void usb_free_buffer(struct iio_buffer_pdata *buf) } static int usb_enable_buffer(struct iio_buffer_pdata *pdata, - size_t nb_samples, bool enable) + size_t nb_samples, bool enable, bool cyclic) { - return iiod_client_enable_buffer(pdata->pdata, nb_samples, enable); + return iiod_client_enable_buffer(pdata->pdata, nb_samples, enable, cyclic); } static struct iio_block_pdata * From b8f3ea0be33ee90a7dfc4614752615acb65dc978 Mon Sep 17 00:00:00 2001 From: Paul Cercueil Date: Mon, 11 Dec 2023 11:50:34 +0100 Subject: [PATCH 04/11] Rework cyclic block enqueue when talking to v0.x IIOD Instead of continuously sending the same block again and again, set the "cyclic" parameter of the .enable_buffer() backend callback to the value of the "cyclic" parameter of the last call to iio_buffer_enqueue(). Signed-off-by: Paul Cercueil --- block.c | 18 +++--------------- buffer.c | 1 + iio-private.h | 3 +++ 3 files changed, 7 insertions(+), 15 deletions(-) diff --git a/block.c b/block.c index 8a263100e..f6b0f9bbc 100644 --- a/block.c +++ b/block.c @@ -19,9 +19,8 @@ struct iio_block { size_t size; void *data; - struct iio_task_token *token, *old_token; + struct iio_task_token *token; size_t bytes_used; - bool cyclic; }; struct iio_block * @@ -83,9 +82,6 @@ void iio_block_destroy(struct iio_block *block) struct iio_buffer *buf = block->buffer; const struct iio_backend_ops *ops = buf->dev->ctx->ops; - /* Stop the cyclic task */ - block->cyclic = false; - if (block->token) { iio_task_cancel(block->token); iio_task_sync(block->token, 0); @@ -132,20 +128,12 @@ int iio_block_io(struct iio_block *block) if (!iio_device_is_tx(block->buffer->dev)) return iio_block_read(block); - if (block->old_token) - iio_task_sync(block->old_token, 0); - - if (block->cyclic) { - block->old_token = block->token; - block->token = iio_task_enqueue(block->buffer->worker, block); - } - return iio_block_write(block); } int iio_block_enqueue(struct iio_block *block, size_t bytes_used, bool cyclic) { - const struct iio_buffer *buffer = block->buffer; + struct iio_buffer *buffer = block->buffer; const struct iio_device *dev = buffer->dev; const struct iio_backend_ops *ops = dev->ctx->ops; @@ -164,7 +152,7 @@ int iio_block_enqueue(struct iio_block *block, size_t bytes_used, bool cyclic) } block->bytes_used = bytes_used; - block->cyclic = cyclic; + buffer->cyclic = cyclic; block->token = iio_task_enqueue(buffer->worker, block); return iio_err(block->token); diff --git a/buffer.c b/buffer.c index e8b35afd7..654fd9490 100644 --- a/buffer.c +++ b/buffer.c @@ -51,6 +51,7 @@ static int iio_buffer_set_enabled(const struct iio_buffer *buf, bool enabled) if (buf->block_size) { sample_size = iio_device_get_sample_size(buf->dev, buf->mask); nb_samples = buf->block_size / sample_size; + cyclic = buf->cyclic; } if (ops->enable_buffer) diff --git a/iio-private.h b/iio-private.h index 5d6ec2951..575b5ac73 100644 --- a/iio-private.h +++ b/iio-private.h @@ -146,7 +146,10 @@ struct iio_buffer { struct iio_task *worker; + /* These two fields are set by the last block created. They are only + * used when communicating with v0.x IIOD. */ size_t block_size; + bool cyclic; struct iio_attr_list attrlist; From c9cd6563ef37d943b4b314d3425872f9ffb4d6a6 Mon Sep 17 00:00:00 2001 From: "Travis F. Collins" Date: Wed, 29 Nov 2023 20:59:34 -0700 Subject: [PATCH 05/11] Add initial testing with coverage Signed-off-by: Travis F. Collins --- CMakeLists.txt | 34 +++++++++++++++++++++++++++++++++ tests/CMakeLists.txt | 4 ++++ tests/standalone/CMakeLists.txt | 6 ++++++ tests/standalone/test_version.c | 22 +++++++++++++++++++++ 4 files changed, 66 insertions(+) create mode 100644 tests/CMakeLists.txt create mode 100644 tests/standalone/CMakeLists.txt create mode 100644 tests/standalone/test_version.c diff --git a/CMakeLists.txt b/CMakeLists.txt index 9f57a5fbd..074552e68 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -653,6 +653,40 @@ add_subdirectory(bindings) option(WITH_MAN "Generate on-line reference manuals (man pages)" OFF) add_subdirectory(man) +# Testing +option(TESTS "Enabling unit testing" OFF) +option(TESTS_DEBUG "Enable test debugging" OFF) +option(TESTS_COVERAGE "Enable coverage tracing when testing" OFF) +if(TESTS) + include(CTest) + enable_testing() + add_subdirectory(tests) + if(TESTS_COVERAGE) + if(NOT ${CMAKE_SYSTEM_NAME} MATCHES "Linux") + message(FATAL_ERROR "Coverage only supported on Linux") + endif() + # Verify that lcov is installed + find_program(LCOV_PATH lcov) + if(NOT LCOV_PATH) + message(FATAL_ERROR "lcov not found! Aborting...") + endif() + SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -g -O0 -Wall -fprofile-arcs -ftest-coverage") + SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -g -O0 -Wall -W -fprofile-arcs -ftest-coverage") + SET(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -fprofile-arcs -ftest-coverage") + target_compile_options(iio PRIVATE --coverage) + target_link_libraries(iio PRIVATE --coverage) + + # Add custom target to run tests with coverage + add_custom_target( + coverage + COMMAND ${CMAKE_CTEST_COMMAND} --progress && lcov -c -d ${CMAKE_CURRENT_SOURCE_DIR} -o main_coverage.info && genhtml main_coverage.info -o coverage + WORKING_DIRECTORY ${CMAKE_BINARY_DIR}) + + message(STATUS "Coverage flags enabled") + endif() + +endif() + include(cmake/Install.cmake) # Add uninstall target diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt new file mode 100644 index 000000000..a5cf91601 --- /dev/null +++ b/tests/CMakeLists.txt @@ -0,0 +1,4 @@ +if(TESTS_DEBUG) + add_definitions(-DTESTS_DEBUG) +endif() +add_subdirectory(standalone) diff --git a/tests/standalone/CMakeLists.txt b/tests/standalone/CMakeLists.txt new file mode 100644 index 000000000..050864c8d --- /dev/null +++ b/tests/standalone/CMakeLists.txt @@ -0,0 +1,6 @@ +add_executable(TestVersion test_version.c) +target_link_libraries(TestVersion LINK_PRIVATE iio) +add_test( + NAME TestVersion + COMMAND TestVersion + WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}) diff --git a/tests/standalone/test_version.c b/tests/standalone/test_version.c new file mode 100644 index 000000000..5d9c58525 --- /dev/null +++ b/tests/standalone/test_version.c @@ -0,0 +1,22 @@ +#include +#include + +#include "iio/iio.h" + +int main() { + + char *expected_version = "1.0"; + char actual_version[10]; + + sprintf(actual_version, "%u.%u", iio_context_get_version_major(NULL), + iio_context_get_version_minor(NULL)); + +#ifdef TESTS_DEBUG + printf("Expected version: %s\n", expected_version); + printf("Actual version: %s\n", actual_version); +#endif + + assert(strcmp(expected_version, actual_version) == 0); + + return 0; +} \ No newline at end of file From f105ee95b9d11d6a07351e033382a59bb889f871 Mon Sep 17 00:00:00 2001 From: "Travis F. Collins" Date: Wed, 29 Nov 2023 20:59:53 -0700 Subject: [PATCH 06/11] Add github action for tests Signed-off-by: Travis F. Collins --- .github/workflows/tests.yml | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) create mode 100644 .github/workflows/tests.yml diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml new file mode 100644 index 000000000..92eb3fe3e --- /dev/null +++ b/.github/workflows/tests.yml @@ -0,0 +1,33 @@ +name: Testing + +on: [push, pull_request] + +jobs: + linux-test: + runs-on: ubuntu-latest + container: + image: tfcollins/libiio_ubuntu_22_04-ci:latest + steps: + - uses: actions/checkout@v2 + - name: Set up Python 3.7 + uses: actions/setup-python@v2 + with: + python-version: 3.7 + - name: Install dependencies + run: | + apt update + apt install -y lcov + - name: Build and Test + run: | + mkdir build + cd build + cmake .. -DTESTS=ON -DTESTS_DEBUG=ON -DTESTS_COVERAGE=ON + make + make coverage + + # - name: Upload coverage to Codecov + # uses: codecov/codecov-action@v1 + # with: + # token: ${{ secrets.CODECOV_TOKEN }} + # file: ./build/coverage.xml + # flags: unittests From 37f648330e50a9744091b6aaa5204bf39701889b Mon Sep 17 00:00:00 2001 From: "Travis F. Collins" Date: Thu, 30 Nov 2023 13:51:01 -0700 Subject: [PATCH 07/11] Add TESTS flags to build readme Signed-off-by: Travis F. Collins --- README_BUILD.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/README_BUILD.md b/README_BUILD.md index d6cb32fa6..cf0ab76d3 100644 --- a/README_BUILD.md +++ b/README_BUILD.md @@ -59,6 +59,9 @@ Cmake Options | Default | Target | Description `WITH_GCOV` | OFF | Linux | Build with gcov profiling flags | `OSX_FRAMEWORK` | ON | Mac | OS X frameworks provide the interfaces you need to write software for Mac. | `OSX_PACKAGE` | ON | Mac | Create a OSX package for installation on local and other machines | +`TESTS` | OFF | All | Build tests and enable tests targets | +`TESTS_DEBUG` | OFF | All | Build tests with debug outputs | +`TESTS_COVERAGE` | OFF | Linux | Enable debug flags and coverage report generation | Which backends the library supports is dependent on the build system, but can be overridden. (If cmake finds libusb, it will use it, unless turned off manually) From c1379a7fae3f89b45e430a1a790631030d3c89dc Mon Sep 17 00:00:00 2001 From: "Travis F. Collins" Date: Mon, 4 Dec 2023 12:01:02 -0700 Subject: [PATCH 08/11] Add system tests for python buffers Signed-off-by: Travis F. Collins --- bindings/python/requirements_test.txt | 3 + bindings/python/tests/tests_ad9361.py | 152 ++++++++++++++++++++++++++ 2 files changed, 155 insertions(+) create mode 100644 bindings/python/requirements_test.txt create mode 100644 bindings/python/tests/tests_ad9361.py diff --git a/bindings/python/requirements_test.txt b/bindings/python/requirements_test.txt new file mode 100644 index 000000000..8cfe497b0 --- /dev/null +++ b/bindings/python/requirements_test.txt @@ -0,0 +1,3 @@ +pytest +scipy +numpy \ No newline at end of file diff --git a/bindings/python/tests/tests_ad9361.py b/bindings/python/tests/tests_ad9361.py new file mode 100644 index 000000000..6e276036c --- /dev/null +++ b/bindings/python/tests/tests_ad9361.py @@ -0,0 +1,152 @@ +import pytest + +import iio +import numpy as np +from scipy import signal + +import os + + +def gen_data(fs, fc): + N = 2**12 + ts = 1 / float(fs) + t = np.arange(0, N * ts, ts) + i = np.cos(2 * np.pi * t * fc) * 2**14 + q = np.sin(2 * np.pi * t * fc) * 2**14 + iq = i + 1j * q + return iq + + +def estimate_freq(x, fs): + f, Pxx_den = signal.periodogram(x, fs) + idx = np.argmax(Pxx_den) + return f[idx] + + +## Streaming not supported yet +# def stream_based_tx(buf_tx, iq): +# # Stream version (Does not support cyclic?) +# tx_stream = iio.Stream(buf_tx, 1024) +# block_tx = next(tx_stream) +# block_tx.write(iq) +# block_tx.enqueue(None, True) +# buf_tx.enabled = True + + +def block_based_tx_chans(block_tx, buf_tx, mask_tx, ib, qb): + # Block version channel based + mask_tx.channels[0].write(block_tx, ib) + mask_tx.channels[1].write(block_tx, qb) + block_tx.enqueue(None, True) + buf_tx.enabled = True + + +def block_based_tx_single(block_tx, buf_tx, ib, qb): + iqb = np.stack((ib, qb), axis=-1) + iqb = iqb.flatten() + iqb = bytearray(iqb) + + # Block version single write + block_tx.write(iqb) + block_tx.enqueue(None, True) + buf_tx.enabled = True + + +@pytest.mark.parametrize("tx_buffer_modes", ["dds", "chans", "single"]) +@pytest.mark.parametrize("rx_buffer_modes", ["chans", "single"]) +def test_ad9361_buffers(tx_buffer_modes, rx_buffer_modes): + fs = 4e6 + lo = 1e9 + fc = 5e5 + + uri = os.getenv("URI", "ip:analog.local") + + ctx = iio.Context("ip:analog.local") + dev = ctx.find_device("ad9361-phy") + dev_rx = ctx.find_device("cf-ad9361-lpc") + dev_tx = ctx.find_device("cf-ad9361-dds-core-lpc") + + chan = dev.find_channel("voltage0") + chan.attrs["sampling_frequency"].value = str(int(fs)) + chan.attrs["rf_bandwidth"].value = str(int(fs)) + + achan = dev.find_channel("altvoltage0", True) + achan.attrs["frequency"].value = str(int(lo)) + achan = dev.find_channel("altvoltage1", True) + achan.attrs["frequency"].value = str(int(lo)) + + dev.debug_attrs["loopback"].value = "1" + + if tx_buffer_modes == "dds": + # DDS + for N in [1]: + for IQ in ["I", "Q"]: + chan = f"TX1_{IQ}_F{N}" + dds = dev_tx.find_channel(chan, True) + if not dds: + raise Exception(f"Could not find channel {chan}") + dds.attrs["frequency"].value = str(int(fc)) + dds.attrs["scale"].value = "0.2" + if IQ == "I": + dds.attrs["phase"].value = "90000" + else: + dds.attrs["phase"].value = "0.0" + + ## Buffer stuff + + # RX Side + chan1 = dev_rx.find_channel("voltage0") + chan2 = dev_rx.find_channel("voltage1") + mask = iio.ChannelsMask(dev_rx) + mask.channels = [chan1, chan2] + + buf = iio.Buffer(dev_rx, mask) + stream = iio.Stream(buf, 1024) + + if tx_buffer_modes != "dds": + # TX Side + chan1_tx = dev_tx.find_channel("voltage0", True) + chan2_tx = dev_tx.find_channel("voltage1", True) + mask_tx = iio.ChannelsMask(dev_tx) + mask_tx.channels = [chan1_tx, chan2_tx] + + # Create a sinewave waveform + fc = 15e5 + iq = gen_data(fs, fc) + # Convert iq to interleaved int16 byte array + ib = np.array(iq.real, dtype=np.int16) + qb = np.array(iq.imag, dtype=np.int16) + + # Send data to iio + buf_tx = iio.Buffer(dev_tx, mask_tx) + block_tx = iio.Block(buf_tx, len(ib)) + + if tx_buffer_modes == "chans": + block_based_tx_chans(block_tx, buf_tx, mask_tx, ib, qb) + + if tx_buffer_modes == "single": + block_based_tx_single(block_tx, buf_tx, ib, qb) + + # Clear buffer queue + for r in range(10): + block = next(stream) + + for r in range(20): + block = next(stream) + + # Single buffer read + if rx_buffer_modes == "single": + x = np.frombuffer(block.read(), dtype=np.int16) + x = x[0::2] + 1j * x[1::2] + else: + # Buffer read by channel + re = mask.channels[0].read(block) + re = np.frombuffer(re, dtype=np.int16) + im = mask.channels[1].read(block) + im = np.frombuffer(im, dtype=np.int16) + x = re + 1j * im + + freq = estimate_freq(x, fs) + print(f"Estimated freq: {freq/1e6} MHz | Expected freq: {fc/1e6} MHz") + + assert np.abs(freq - fc) < 1e3 From 7a2dd7e8158d79274be5a20de9a2cc7248725559 Mon Sep 17 00:00:00 2001 From: "Travis F. Collins" Date: Fri, 8 Dec 2023 08:56:13 -0700 Subject: [PATCH 09/11] Update URI in test config Signed-off-by: Travis F. Collins --- bindings/python/tests/tests_ad9361.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bindings/python/tests/tests_ad9361.py b/bindings/python/tests/tests_ad9361.py index 6e276036c..39508b16c 100644 --- a/bindings/python/tests/tests_ad9361.py +++ b/bindings/python/tests/tests_ad9361.py @@ -61,7 +61,7 @@ def test_ad9361_buffers(tx_buffer_modes, rx_buffer_modes): uri = os.getenv("URI", "ip:analog.local") - ctx = iio.Context("ip:analog.local") + ctx = iio.Context(uri) dev = ctx.find_device("ad9361-phy") dev_rx = ctx.find_device("cf-ad9361-lpc") dev_tx = ctx.find_device("cf-ad9361-dds-core-lpc") From 9902e7686a9de8ee69f491e97bb4e1596358a12a Mon Sep 17 00:00:00 2001 From: "Travis F. Collins" Date: Fri, 8 Dec 2023 14:49:38 -0700 Subject: [PATCH 10/11] Add C based hardware tests for AD9361 buffers Signed-off-by: Travis F. Collins --- tests/CMakeLists.txt | 2 + tests/hardware/CMakeLists.txt | 6 + tests/hardware/test_ad9364.c | 220 ++++++++++++++++++++++++++++++++ tests/standalone/test_version.c | 1 + 4 files changed, 229 insertions(+) create mode 100644 tests/hardware/CMakeLists.txt create mode 100644 tests/hardware/test_ad9364.c diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index a5cf91601..ae872d585 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -1,4 +1,6 @@ +add_definitions(-UNDEBUG) if(TESTS_DEBUG) add_definitions(-DTESTS_DEBUG) endif() add_subdirectory(standalone) +add_subdirectory(hardware) diff --git a/tests/hardware/CMakeLists.txt b/tests/hardware/CMakeLists.txt new file mode 100644 index 000000000..b7330d824 --- /dev/null +++ b/tests/hardware/CMakeLists.txt @@ -0,0 +1,6 @@ +add_executable(TestBufferAD9361 test_ad9364.c) +target_link_libraries(TestBufferAD9361 LINK_PRIVATE iio) +add_test( + NAME TestBufferAD9361 + COMMAND TestBufferAD9361 + WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}) diff --git a/tests/hardware/test_ad9364.c b/tests/hardware/test_ad9364.c new file mode 100644 index 000000000..d5a79bec7 --- /dev/null +++ b/tests/hardware/test_ad9364.c @@ -0,0 +1,220 @@ +// This test is designed to verify data from transmit to receive buffers by +// using a ramp signal. The ramp signal is generated on the TX side and then +// received on the RX side. The RX side then checks to make sure the ramp is +// continuous within the buffer but not across buffers. However, every buffer is +// checked to make sure the ramp is continuous. + +#include +#include +#include +#include + +#include "iio/iio.h" + +#ifndef TESTS_DEBUG +#define TESTS_DEBUG 0 +#endif + +// Use (void) to silence unused warnings. +#define assertm(exp, msg) assert(((void)msg, exp)) + +#define dprintf(fmt, ...) \ + do { \ + if (TESTS_DEBUG>0) \ + fprintf(stderr, fmt, ##__VA_ARGS__); \ + } while (0) + +// User Set +#define N_TX_SAMPLES 128 +#define RX_OVERSAMPLE 4 +#define SUCCESSIVE_BUFFER_TO_CHECK 31 +#define N_RX_BLOCKS 4 + +// Calculated/Constant +#define N_RX_SAMPLES N_TX_SAMPLES *RX_OVERSAMPLE +#define N_CHANNELS 2 +#define BYTES_PER_SAMPLE 2 + +struct iio_context *ctx; +struct iio_device *phy, *rx, *tx; +const struct iio_attr *attr; +struct iio_channel *chn; +struct iio_channels_mask *txmask, *rxmask; +struct iio_buffer *txbuf, *rxbuf; +struct iio_block *txblock; +const struct iio_block *rxblock; +struct iio_stream *rxstream; + +int main() { + + int err; + + const char *uri = getenv("URI_AD9361"); + if (uri == NULL) + exit(0); // Cant find anything don't run tests + ctx = iio_create_context(NULL, uri); + + phy = iio_context_find_device(ctx, "ad9361-phy"); + assertm(phy, "Unable to find AD9361-phy device"); + rx = iio_context_find_device(ctx, "cf-ad9361-lpc"); + assertm(rx, "Unable to find RX device"); + tx = iio_context_find_device(ctx, "cf-ad9361-dds-core-lpc"); + assertm(tx, "Unable to find TX device"); + + // Configure device into loopback mode + attr = iio_device_find_debug_attr(phy, "loopback"); + assertm(attr, "Unable to find loopback attribute"); + iio_attr_write_string(attr, "1"); + + // TX Side + txmask = iio_create_channels_mask(iio_device_get_channels_count(tx)); + assertm(txmask, "Unable to create TX mask"); + + chn = iio_device_find_channel(tx, "voltage0", true); + assertm(chn, "Unable to find TX channel"); + iio_channel_enable(chn, txmask); + chn = iio_device_find_channel(tx, "voltage1", true); + assertm(chn, "Unable to find TX channel"); + iio_channel_enable(chn, txmask); + + txbuf = iio_device_create_buffer(tx, 0, txmask); + assertm(txbuf, "Unable to create TX buffer"); + + txblock = iio_buffer_create_block(txbuf, N_TX_SAMPLES * BYTES_PER_SAMPLE * + N_CHANNELS); + assertm(txblock, "Unable to create TX block"); + + // Generate ramp signal on both I and Q channels + int16_t *p_dat, *p_end; + ptrdiff_t p_inc; + int16_t idx = 0; + + p_end = iio_block_end(txblock); + p_inc = iio_device_get_sample_size(tx, txmask); + chn = iio_device_find_channel(tx, "voltage0", true); + + for (p_dat = iio_block_first(txblock, chn); p_dat < p_end; + p_dat += p_inc / sizeof(*p_dat)) { + // Bitshift 4 bits up. During loopback hardware will shift back 4 bits + p_dat[0] = idx << 4; + p_dat[1] = idx << 4; + idx++; + } + iio_block_enqueue(txblock, 0, true); + iio_buffer_enable(txbuf); + sleep(2); + + // RX Side + rxmask = iio_create_channels_mask(iio_device_get_channels_count(rx)); + assertm(rxmask, "Unable to create RX mask"); + + chn = iio_device_find_channel(rx, "voltage0", false); + assertm(chn, "Unable to find RX channel voltage0"); + iio_channel_enable(chn, rxmask); + chn = iio_device_find_channel(rx, "voltage1", false); + assertm(chn, "Unable to find RX channel voltage1"); + iio_channel_enable(chn, rxmask); + + rxbuf = iio_device_create_buffer(rx, 0, rxmask); + assertm(rxbuf, "Unable to create RX buffer"); + + rxstream = iio_buffer_create_stream(rxbuf, N_RX_BLOCKS, N_RX_SAMPLES); + assertm(rxstream, "Unable to create RX stream"); + + p_inc = iio_device_get_sample_size(rx, rxmask); + chn = iio_device_find_channel(rx, "voltage0", false); + + bool found_start = false; + int16_t ramp_indx = 0; + + // Create check vector + bool ramp_found_check_vector[SUCCESSIVE_BUFFER_TO_CHECK]; + bool continuous_check_vector[SUCCESSIVE_BUFFER_TO_CHECK]; + + // Remove first few blocks as they might be old + for (int i = 0; i < 30; i++) { + rxblock = iio_stream_get_next_block(rxstream); + dprintf("Removing block %d\n", i); + } + + // Check several buffers to make sure no glitches occurred + for (int i = 0; i < SUCCESSIVE_BUFFER_TO_CHECK; i++) { + + dprintf("Checking buffer %d of %d\n", i + 1, SUCCESSIVE_BUFFER_TO_CHECK); + + rxblock = iio_stream_get_next_block(rxstream); + p_end = iio_block_end(rxblock); + + // Within a block data should be continuous but not necessarily across + // blocks + found_start = false; + continuous_check_vector[i] = true; // assume good + ramp_indx = 0; + + for (p_dat = iio_block_first(rxblock, chn); p_dat < p_end; + p_dat += p_inc / sizeof(*p_dat)) { + + // Locate top of ramp + if (p_dat[0] == (N_TX_SAMPLES - 1) && p_dat[1] == (N_TX_SAMPLES - 1) && + !found_start) { + found_start = true; + continue; // Wrap to ramp restarts on next sample + } + + // Make sure ramp is continuous + if (found_start) { + dprintf("Expected: %d\n", ramp_indx); + dprintf("Actual: %d, %d (I, Q)\n\n", p_dat[0], p_dat[1]); + if (p_dat[0] != ramp_indx && p_dat[1] != ramp_indx) { + dprintf("--->Expected: %d (Buffer %d)\n", ramp_indx, i); + dprintf("--->Actual: %d, %d (I, Q) [Buffer %d]\n\n", p_dat[0], + p_dat[1], i); + dprintf("\n\n"); + continuous_check_vector[i] = false; + } + if (ramp_indx == (N_TX_SAMPLES - 1)) { + ramp_indx = 0; + } else + ramp_indx++; + } + } + + ramp_found_check_vector[i] = found_start; + if (!found_start) + continuous_check_vector[i] = false; + } + + // Examine check vector + bool failed_c1 = false; + bool failed_c2 = false; + dprintf("1 == Check Passed, 0 == Failed\n"); + dprintf("Ramp Check, Contiguous Check (Buffer #)\n"); + + for (int i = 0; i < SUCCESSIVE_BUFFER_TO_CHECK; i++) { + dprintf("%d, %d (%d)\n", ramp_found_check_vector[i], + continuous_check_vector[i], i); + if (!ramp_found_check_vector[i]) + failed_c1 = true; + if (!continuous_check_vector[i]) + failed_c2 = true; + } + dprintf("\n"); + + assertm(!failed_c1, "Ramp was not found in all buffers"); + assertm(!failed_c2, "Ramp was not contiguous in all buffers"); + + iio_stream_destroy(rxstream); + iio_buffer_destroy(rxbuf); + + // // Manual check RX (disable asserts above first) + // printf("Open up the time scope to see data. Should be a ramp from + // 0->%d\n", + // idx - 1); + // sleep(40); + + // Cleanup + iio_block_destroy(txblock); + iio_buffer_destroy(txbuf); + + return 0; +} \ No newline at end of file diff --git a/tests/standalone/test_version.c b/tests/standalone/test_version.c index 5d9c58525..8ba916863 100644 --- a/tests/standalone/test_version.c +++ b/tests/standalone/test_version.c @@ -1,5 +1,6 @@ #include #include +#include #include "iio/iio.h" From c55f0d681e95c489237636c07d2343ea4ea938b6 Mon Sep 17 00:00:00 2001 From: "Travis F. Collins" Date: Fri, 15 Dec 2023 10:38:40 -0700 Subject: [PATCH 11/11] Require dependent GCOV flags Signed-off-by: Travis F. Collins --- .github/workflows/tests.yml | 2 +- CMakeLists.txt | 4 ++++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 92eb3fe3e..b748d2397 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -21,7 +21,7 @@ jobs: run: | mkdir build cd build - cmake .. -DTESTS=ON -DTESTS_DEBUG=ON -DTESTS_COVERAGE=ON + cmake .. -DTESTS=ON -DTESTS_DEBUG=ON -DTESTS_COVERAGE=ON -DWITH_GCOV=ON make make coverage diff --git a/CMakeLists.txt b/CMakeLists.txt index 074552e68..50d628d6c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -662,6 +662,10 @@ if(TESTS) enable_testing() add_subdirectory(tests) if(TESTS_COVERAGE) + if(NOT WITH_GCOV) + message(FATAL_ERROR "Coverage report generation only supported with WITH_GCOV also enabled") + endif() + if(NOT ${CMAKE_SYSTEM_NAME} MATCHES "Linux") message(FATAL_ERROR "Coverage only supported on Linux") endif()