How should I configure it to achieve Gazell's maximum throughput?

Hi, I recently purchased an nRF52833DK and tested the attached code. When using Gazell, the maximum throughput I achieved was around 85,333 bps. However, according to the explanation in the following link:

https://devzone.nordicsemi.com/f/nordic-q-a/17806/gazell-max-throughput-multilink

it appears that the maximum throughput is calculated as follows:

For example, with:

  • nrf_gzll_set_timeslots_per_channel = 2
  • nrf_gzll_set_timeslot_period = 600 µs
  • PayloadSize = 32

the calculation becomes:

Example code attached : 

=====================================================================================================

/*
 * Copyright (c) 2021 Nordic Semiconductor ASA
 *
 * SPDX-License-Identifier: LicenseRef-Nordic-5-Clause
 */

#include <zephyr/kernel.h>
#include <nrf_gzll.h>
#include <gzll_glue.h>
#include <dk_buttons_and_leds.h>

#include <zephyr/logging/log.h>

#define CONFIG_GZLL_TX_STATISTICS 1

// LOG_MODULE_REGISTER(app);

LOG_MODULE_REGISTER(gzll_device, LOG_LEVEL_INF);

/* Pipe 0 is used in this example. */
#define PIPE_NUMBER 0


/* 1-byte payload length is used when transmitting. */
#define TX_PAYLOAD_LENGTH 32

/* Maximum number of transmission attempts */
#define MAX_TX_ATTEMPTS 30



/* Gazell Link Layer TX result structure */
struct gzll_tx_result {
    bool success;
    uint32_t pipe;
    nrf_gzll_device_tx_info_t info;
};

/* Payload to send to Host. */
static uint8_t data_payload[TX_PAYLOAD_LENGTH];

/* Placeholder for received ACK payloads from Host. */
static uint8_t ack_payload[NRF_GZLL_CONST_MAX_PAYLOAD_LENGTH];

#ifdef CONFIG_GZLL_TX_STATISTICS
/* Struct containing transmission statistics. */
static nrf_gzll_tx_statistics_t statistics;
static nrf_gzll_tx_statistics_t statistics_2;
#endif

/* Gazell Link Layer TX result message queue */
K_MSGQ_DEFINE(gzll_msgq,
          sizeof(struct gzll_tx_result),
          1,
          sizeof(uint32_t));

/* Main loop semaphore */
static K_SEM_DEFINE(main_sem, 0, 1);

static struct k_work gzll_work;

static void gzll_tx_result_handler(struct gzll_tx_result *tx_result);
static void gzll_work_handler(struct k_work *work);



void make_send_packet(void)
{

    uint8_t index = 4;
    float temp = 10.1f;

    data_payload[0] = ~dk_get_buttons();
    data_payload[1] = 0xAA;
    data_payload[2] = 0x33;
    data_payload[3] = 0xAA;
   

    for (int i = 0; i < 7; i++)
    {
        memcpy(&data_payload[index], (void *)&temp, sizeof(float));
        index += sizeof(float);
        temp += 10.0f;
    }
}

static void gzll_error_code_check(nrf_gzll_error_code_t error_code)
{
    switch (error_code)
    {
    case NRF_GZLL_ERROR_CODE_NO_ERROR:
        LOG_ERR("NRF_GZLL_ERROR_CODE_NO_ERROR");
        break;

    case NRF_GZLL_ERROR_CODE_FAILED_TO_INITIALIZE:
        LOG_ERR("NRF_GZLL_ERROR_CODE_FAILED_TO_INITIALIZE");
        break;

    case NRF_GZLL_ERROR_CODE_ATTEMPTED_TO_CONFIGURE_WHEN_ENABLED:
        LOG_ERR("NRF_GZLL_ERROR_CODE_ATTEMPTED_TO_CONFIGURE_WHEN_ENABLED");
        break;

    case NRF_GZLL_ERROR_CODE_POINTER_IS_NULL:
        LOG_ERR("NRF_GZLL_ERROR_CODE_POINTER_IS_NULL");
        break;

    case NRF_GZLL_ERROR_CODE_INVALID_PIPE:
        LOG_ERR("NRF_GZLL_ERROR_CODE_INVALID_PIPE");
        break;

    case NRF_GZLL_ERROR_CODE_INVALID_MODE:
        LOG_ERR("NRF_GZLL_ERROR_CODE_INVALID_MODE");
        break;

    case NRF_GZLL_ERROR_CODE_INVALID_PAYLOAD_LENGTH:
        LOG_ERR("NRF_GZLL_ERROR_CODE_INVALID_PAYLOAD_LENGTH");
        break;

    case NRF_GZLL_ERROR_CODE_INVALID_CHANNEL_TABLE_SIZE:
        LOG_ERR("NRF_GZLL_ERROR_CODE_INVALID_CHANNEL_TABLE_SIZE");
        break;
    case NRF_GZLL_ERROR_CODE_INSUFFICIENT_PACKETS_AVAILABLE:
        LOG_ERR("NRF_GZLL_ERROR_CODE_INSUFFICIENT_PACKETS_AVAILABLE");
        break;

    case NRF_GZLL_ERROR_CODE_ATTEMPTED_TO_ADD_TO_FULL_FIFO:
        LOG_ERR("NRF_GZLL_ERROR_CODE_ATTEMPTED_TO_ADD_TO_FULL_FIFO");
        break;

    case NRF_GZLL_ERROR_CODE_NO_SPACE_IN_RX_FIFO_FOR_ACK:
        LOG_ERR("NRF_GZLL_ERROR_CODE_NO_SPACE_IN_RX_FIFO_FOR_ACK");
        break;

    case NRF_GZLL_ERROR_CODE_ATTEMPTED_TO_FETCH_FROM_EMPTY_FIFO:
        LOG_ERR("NRF_GZLL_ERROR_CODE_ATTEMPTED_TO_FETCH_FROM_EMPTY_FIFO");
        break;
    case NRF_GZLL_ERROR_CODE_ATTEMPTED_TO_FLUSH_WHEN_ENABLED:
        LOG_ERR("NRF_GZLL_ERROR_CODE_ATTEMPTED_TO_FLUSH_WHEN_ENABLED");
        break;

    case NRF_GZLL_ERROR_CODE_INVALID_PARAMETER:
        LOG_ERR("NRF_GZLL_ERROR_CODE_INVALID_PARAMETER");
        break;

    case NRF_GZLL_ERROR_CODE_INTERNAL_ASSERT_OCCURRED:
        LOG_ERR("NRF_GZLL_ERROR_CODE_INTERNAL_ASSERT_OCCURRED");
        break;

    case NRF_GZLL_ERROR_CODE_CALLBACK_NOT_IMPLEMENTED:
        LOG_ERR("NRF_GZLL_ERROR_CODE_CALLBACK_NOT_IMPLEMENTED");
        break;
    case NRF_GZLL_ERROR_CODE_INVALID_ADDRESS:
        LOG_ERR("NRF_GZLL_ERROR_CODE_INVALID_ADDRESS");
        break;
    case NRF_GZLL_ERROR_CODE_NUMBER_OF_ERROR_CODES:
        LOG_ERR("NRF_GZLL_ERROR_CODE_NUMBER_OF_ERROR_CODES");
        break;
    }
}


// #define SEND_THREAD_STACK_SIZE 500
// #define SEND_THREAD_PRIORITY 5

//  void send_thread(void *p1, void *p2, void *p3)
// {
//  bool result_value;
//  nrf_gzll_error_code_t err_code;

//  while (true)
//  {

//      make_send_packet();
//      result_value = nrf_gzll_add_packet_to_tx_fifo(0, data_payload, TX_PAYLOAD_LENGTH);
//      if (!result_value)
//      {
//          err_code = nrf_gzll_get_error_code();
//          gzll_error_code_check(err_code);
//      }
//      k_sleep(K_MSEC(1));
//  }
// }




// K_THREAD_STACK_DEFINE(send_thread_stack_area, SEND_THREAD_STACK_SIZE);
// struct k_thread send_thread_data;





int main(void)
{
    int err;
    bool result_value;
    uint32_t default_value;
    nrf_gzll_error_code_t err_code;

    k_work_init(&gzll_work, gzll_work_handler);

    err = dk_leds_init();
    if (err) {
        LOG_ERR("Cannot initialize LEDs (err: %d)", err);
        return 0;
    }

    err = dk_buttons_init(NULL);
    if (err) {
        LOG_ERR("Cannot initialize buttons (err: %d)", err);
        return 0;
    }

    /* Initialize Gazell Link Layer glue */
    result_value = gzll_glue_init();
    if (!result_value) {
        err_code = nrf_gzll_get_error_code();
        gzll_error_code_check(err_code);
        return 0;
    }

    /* Initialize the Gazell Link Layer */
    result_value = nrf_gzll_init(NRF_GZLL_MODE_DEVICE);
    if (!result_value) {
        err_code = nrf_gzll_get_error_code();
        gzll_error_code_check(err_code);
        return 0;
    }

    nrf_gzll_set_max_tx_attempts(MAX_TX_ATTEMPTS);

#ifdef CONFIG_GZLL_TX_STATISTICS
    /* Turn on transmission statistics gathering. */
    result_value = nrf_gzll_tx_statistics_enable(&statistics);
    if (!result_value) {
        err_code = nrf_gzll_get_error_code();
        gzll_error_code_check(err_code);
        return 0;
    }
#endif

   

    default_value = nrf_gzll_get_sync_lifetime();
    LOG_INF("lifetime default : %d", default_value);

    default_value = nrf_gzll_get_datarate();
    LOG_INF("datarate default : %d", default_value);

    default_value = nrf_gzll_get_timeslot_period();
    LOG_INF("timeslot default: %d", default_value);

    default_value = nrf_gzll_get_timeslots_per_channel();
    LOG_INF("per_channel default: %d", default_value);

 
    result_value = nrf_gzll_set_datarate(NRF_GZLL_DATARATE_2MBIT);
    if (!result_value) {
        err_code = nrf_gzll_get_error_code();
        gzll_error_code_check(err_code);
        return 0;
    }
    result_value = nrf_gzll_set_timeslot_period(600);
    if (!result_value) {
        err_code = nrf_gzll_get_error_code();
        gzll_error_code_check(err_code);
        return 0;
    }
    result_value = nrf_gzll_set_timeslots_per_channel(1);
    if (!result_value) {
        err_code = nrf_gzll_get_error_code();
        gzll_error_code_check(err_code);
        return 0;
    }



    make_send_packet();
    result_value = nrf_gzll_add_packet_to_tx_fifo(PIPE_NUMBER, data_payload, TX_PAYLOAD_LENGTH);
    if (!result_value) {
        err_code = nrf_gzll_get_error_code();
        gzll_error_code_check(err_code);
        return 0;
    }

    result_value = nrf_gzll_enable();
    if (!result_value) {
        err_code = nrf_gzll_get_error_code();
        gzll_error_code_check(err_code);
        return 0;
    }

    // k_tid_t my_tid = k_thread_create(&send_thread_data, send_thread_stack_area,
    //  K_THREAD_STACK_SIZEOF(send_thread_stack_area),
    //  send_thread,
    //  NULL, NULL, NULL,
    //  SEND_THREAD_PRIORITY, 0, K_NO_WAIT);

    LOG_INF("Gzll ack payload device example started.");

    int64_t pre_time = k_uptime_get();//n milliseconds.

    while (true) {
        if (k_sem_take(&main_sem, K_FOREVER)) {
            continue;
        }

#ifdef CONFIG_GZLL_TX_STATISTICS
        if (statistics.packets_num >= 1000) {
            uint32_t key = irq_lock();

            int64_t dt = k_uptime_delta(&pre_time);//n milliseconds.

            int32_t total_BITS = (int32_t)(statistics.packets_num*TX_PAYLOAD_LENGTH*8.0);
            int32_t BPS = (int32_t)( (double)total_BITS/(dt*0.001) );
           

            statistics_2 = statistics;

            /* Reset statistics buffers. */
            nrf_gzll_reset_tx_statistics();

            irq_unlock(key);

            /* Print all transmission statistics. */
            LOG_INF("");
            LOG_INF("Total transmitted packets:   %4u",  statistics_2.packets_num);
            LOG_INF("Total transmission time-outs: %03u", statistics_2.timeouts_num);
            LOG_INF("");

            LOG_INF("Total BPS %d", BPS);

            for (uint8_t i = 0; i < nrf_gzll_get_channel_table_size(); i++) {
                LOG_INF(
                "Channel %u: %03u packets transmitted, %03u transmissions failed.",
                i,
                statistics_2.channel_packets[i],
                statistics_2.channel_timeouts[i]);
            }
        }
#endif
    }
}

static void gzll_device_report_tx(bool success,
                  uint32_t pipe,
                  nrf_gzll_device_tx_info_t *tx_info)
{
    int err;
    struct gzll_tx_result tx_result;

    tx_result.success = success;
    tx_result.pipe = pipe;
    tx_result.info = *tx_info;
    err = k_msgq_put(&gzll_msgq, &tx_result, K_NO_WAIT);
    if (!err) {
        /* Get work handler to run */
        k_work_submit(&gzll_work);
    } else {
        LOG_ERR("Cannot put TX result to message queue");
    }
}

void nrf_gzll_device_tx_success(uint32_t pipe, nrf_gzll_device_tx_info_t tx_info)
{
    gzll_device_report_tx(true, pipe, &tx_info);
}

void nrf_gzll_device_tx_failed(uint32_t pipe, nrf_gzll_device_tx_info_t tx_info)
{
    gzll_device_report_tx(false, pipe, &tx_info);
}

void nrf_gzll_disabled(void)
{
}

void nrf_gzll_host_rx_data_ready(uint32_t pipe, nrf_gzll_host_rx_info_t rx_info)
{
}

static void gzll_work_handler(struct k_work *work)
{
    struct gzll_tx_result tx_result;

    /* Process message queue */
    while (!k_msgq_get(&gzll_msgq, &tx_result, K_NO_WAIT)) {
        gzll_tx_result_handler(&tx_result);
    }

    /* Get main loop to run */
    k_sem_give(&main_sem);
}

static void gzll_tx_result_handler(struct gzll_tx_result *tx_result)
{
    int err;
    bool result_value;
    uint32_t ack_payload_length = NRF_GZLL_CONST_MAX_PAYLOAD_LENGTH;

    if (tx_result->success) {
        if (tx_result->info.payload_received_in_ack) {
            /* Pop packet and write first byte of the payload to the GPIO port. */
            result_value = nrf_gzll_fetch_packet_from_rx_fifo(tx_result->pipe,
                                      ack_payload,
                                      &ack_payload_length);
            if (!result_value) {
                LOG_ERR("RX fifo error");
            } else if (ack_payload_length > 0) {
                err = dk_set_leds(ack_payload[0] & DK_ALL_LEDS_MSK);
                if (err) {
                    LOG_ERR("Cannot output LEDs (err: %d)", err);
                }
            }
        }
    } else {
        LOG_ERR("Gazell transmission failed");
    }

    /* Load data payload into the TX queue. */
    // data_payload[0] = ~dk_get_buttons();

    make_send_packet();



    result_value = nrf_gzll_add_packet_to_tx_fifo(tx_result->pipe,
                              data_payload,
                              TX_PAYLOAD_LENGTH);
    if (!result_value) {
        LOG_ERR("TX fifo error");
    }
}
========================================================================================================

However, in the sample code, with the following settings:

  • nrf_gzll_set_datarate = 2 Mbit/s
  • nrf_gzll_set_timeslots_per_channel = 1
  • nrf_gzll_set_timeslot_period = 600 µs
  • PayloadSize = 32

the maximum achievable BPS was only 85,333 bps.

This is exactly 2.5 times slower than the ideal 213,333 bps calculated above.

Is there any way to modify the Gazell ack host/device sample code to achieve the maximum throughput of 213,333 bps? When I tried creating a separate TX thread to push high-speed transmissions, I encountered the NRF_GZLL_ERROR_CODE_ATTEMPTED_TO_ADD_TO_FULL_FIFO error. This suggests that the Gazell driver itself might not be able to reach 213,333 bps. Could this be due to issues with the new NCS version, or perhaps a problem related to the nRF52833 chip revision (e.g., timer or PPI issues)? I would appreciate it if you could review this and let me know.

In summary, please advise if there is a way to achieve maximum throughput via modifications to the Gazell ack host/device sample code (including revised settings), or if this limitation is due to changes in the new NCS or nRF52833DK version.

Parents Reply Children
Related