nRF5340: LC3 initialization causes a reboot due to error: LC3_RESULT_INSUFFICIENT_RESOURCES

Hi,

I am trying to integrate the nRF5340 Audio application (CIS Unicast Server role with bidirectional streaming enabled) from NCS 2.9.0 into on my custom nRF5340 based hardware.
I am encountering an issue that LC3_API 
LC3Initialize() always returns error code -5002: LC3_RESULT_INSUFFICIENT_RESOURCES,  that fianlly cause system to reboot in function audio_system_start():  "Failed to set up codec"


	void audio_system_start(void)
	{
    	...
    	ret = sw_codec_init(sw_codec_cfg);
    	ERR_CHK_MSG(ret, "Failed to set up codec");
    	...
    }

The UART logs before reboot are as below:

[00:02:21.715,393] <dbg> audio_system: audio_system_start: Initializes the FIFOs, codec, and starts           the I2S
[00:02:21.715,454] <dbg> audio_system: audio_headset_configure: sw_codec_cfg.encoder.num_ch: 1,  Mon          o
[00:02:21.715,515] <dbg> audio_system: audio_headset_configure: sw_codec_cfg.decoder.num_ch: 1,  Mon          o
sw_codec_lc3_init: enc_sample_rates:0x3f, dec_sample_rates:0x3f
sw_codec_lc3_init: framesize:0
LC3Initialize: ret = -5002
[00:06:42.279,998] <err> audio_system: Failed to set up codec
[00:06:42.279,998] <err> audio_system: ERR_CHK Err_code: [-5002] @ line: 406
[00:06:42.280,029] <err> os: r0/a1:  0x00000003  r1/a2:  0x00000008  r2/a3:  0x00000007
[00:06:42.280,029] <err> os: r3/a4:  0x00000003 r12/ip:  0x200055d0 r14/lr:  0x0009ce41
[00:06:42.280,059] <err> os:  xpsr:  0x01100000
[00:06:42.280,059] <err> os: s[ 0]:  0x000bc11c  s[ 1]:  0x0009cefd  s[ 2]:  0xffffec76  s[ 3]:  0x0          0000004
[00:06:42.280,090] <err> os: s[ 4]:  0x2006c3c0  s[ 5]:  0x00000001  s[ 6]:  0xffffec76  s[ 7]:  0x2          006c3a0
[00:06:42.280,090] <err> os: s[ 8]:  0x2006c3c0  s[ 9]:  0x000bc11c  s[10]:  0x20057674  s[11]:  0x0          009cf41
[00:06:42.280,120] <err> os: s[12]:  0x2006c3c0  s[13]:  0x000bce14  s[14]:  0xffffec76  s[15]:  0x0          0000196
[00:06:42.280,120] <err> os: fpscr:  0x00000000
[00:06:42.280,120] <err> os: Faulting instruction address (r15/pc): 0x00016fa2
[00:06:42.280,151] <err> os: >>> ZEPHYR FATAL ERROR 3: Kernel oops on CPU 0
[00:06:42.280,181] <err> os: Current thread: 0x20006d90 (CliHandler)
*** Booting MCUboot v2.1.0-dev-12e5ee106034 ***ystem error -- reason 3. Cold rebooting.



The out-of-box Audio application from NCS 2.9.0 can work fine on 5340 Audio DK but not on my custom board. And I did not change memory related configs against the nRF5340 Audio application. The system heap mem pool size was 4096 by default, and I increased it to 8192 but it still returned the same error "LC3_RESULT_INSUFFICIENT_RESOURCES"

Here are the configs regrading memory in my project:
     CONFIG_HEAP_MEM_POOL_SIZE=8192

     CONFIG_MAIN_STACK_SIZE=3072

     CONFIG_SYSTEM_WORKQUEUE_STACK_SIZE=4096

    CONFIG_SYS_HEAP_ALLOC_LOOPS=3
    CONFIG_SYS_HEAP_AUTO=y
    CONFIG_HEAP_MEM_POOL_ADD_SIZE_MQUEUE=1024
    CONFIG_KERNEL_MEM_POOL=y
    CONFIG_NEWLIB_LIBC_MIN_REQUIRED_HEAP_SIZE=2048

Here is the build result:

Memory region         Used Size  Region Size  %age Used
           FLASH:      880324 B     974336 B     90.35%
             RAM:      279652 B       504 KB     54.19%
        IDT_LIST:          0 GB        32 KB      0.00%
Generating files from xxx/build/zephyr/zephyr.elf for board: nrf5340_cpuapp


As you know, in unicast server, LC3 codec will only be intialized when audio_system_start() is called after the remote Unicast client connects it and intrsucts it to start streaming. This is how I orignally met the issue.

To reproduce this issue easily, I added a shell command to call audio_system_start() even if it is not connected to unicast client yet. I can also produce this same issue.

Here is the sw_codec_lc3.c I used, I just added some logs.

/*
 * Copyright (c) 2021 Nordic Semiconductor ASA
 *
 * All rights reserved.
 *
 * SPDX-License-Identifier: Nordic-5-Clause
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 * 1. Redistributions of source code must retain the above copyright notice,
 * this list of conditions and the following disclaimer.
 *
 * 2. Redistributions in binary form, except as embedded into a Nordic
 *    Semiconductor ASA integrated circuit in a product or a software update for
 *    such product, must reproduce the above copyright notice, this list of
 *    conditions and the following disclaimer in the documentation and/or other
 *    materials provided with the distribution.
 *
 * 3. Neither the name of Nordic Semiconductor ASA nor the names of its
 *    contributors may be used to endorse or promote products derived from this
 *    software without specific prior written permission.
 *
 * 4. This software, with or without modification, must only be used with a
 *    Nordic Semiconductor ASA integrated circuit.
 *
 * 5. Any software provided in binary form under this license must not be
 * reverse engineered, decompiled, modified and/or disassembled.
 *
 * THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS
 * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 *
 */

#include <zephyr/kernel.h>
#include <zephyr/sys/printk.h>
#include "sw_codec_lc3.h"
#include "LC3API.h"

#include <zephyr/logging/log.h>
LOG_MODULE_REGISTER(sw_codec_lc3);

#define ENC_BITRATE_WRN_LVL_LOW 24000
#define ENC_BITRATE_WRN_LVL_HIGH 160000

static uint16_t enc_pcm_bytes_req;
static uint32_t m_enc_bitrate;

static LC3EncoderHandle_t enc_handle_ch[CONFIG_LC3_ENC_CHAN_MAX];
static LC3DecoderHandle_t dec_handle_ch[CONFIG_LC3_DEC_CHAN_MAX];

static uint8_t enc_num_instances;
static uint8_t dec_num_instances;

int sw_codec_lc3_enc_run(void const *const pcm_data, uint32_t pcm_data_size, uint32_t enc_bitrate,
			 uint8_t audio_ch, uint16_t lc3_data_buf_size, uint8_t *const lc3_data,
			 uint16_t *const lc3_data_wr_size)
{
	int ret;
	/* Number of PCM bytes read */
	uint16_t bytes_read = 0;

	if (pcm_data_size < enc_pcm_bytes_req) {
		LOG_ERR("Too few PCM samples to encode. Bytes required %d, input is %d",
			enc_pcm_bytes_req, pcm_data_size);
		return -EINVAL;
	}

	if (audio_ch >= enc_num_instances) {
		LOG_ERR("Trying to use audio_ch outside of allowed range");
		return -EINVAL;
	}

	LC3EncodeInput_t LC3EncodeInput = { .PCMData = (void *)pcm_data,
					    .PCMDataLength = pcm_data_size,
					    .bytesRead = bytes_read };

	if (enc_bitrate == LC3_USE_BITRATE_FROM_INIT) {
		LC3EncodeInput.encodeBitrate = m_enc_bitrate;
	} else {
		LC3EncodeInput.encodeBitrate = enc_bitrate;
	}

	LC3EncodeOutput_t LC3EncodeOutput = { .outputData = lc3_data,
					      .outputDataLength = lc3_data_buf_size,
					      .bytesWritten = 0 };

	if (!enc_handle_ch[audio_ch]) {
		LOG_ERR("LC3 enc ch:%d is not initialized", audio_ch);
		return -EPERM;
	}
	ret = LC3EncodeSessionData(enc_handle_ch[audio_ch], &LC3EncodeInput, &LC3EncodeOutput);
	if (ret) {
		return ret;
	}

	*lc3_data_wr_size = LC3EncodeOutput.bytesWritten;

	return 0;
}

int sw_codec_lc3_dec_run(uint8_t const *const lc3_data, uint16_t lc3_data_size,
			 uint16_t pcm_data_buf_size, uint8_t audio_ch, void *const pcm_data,
			 uint16_t *const pcm_data_wr_size, bool bad_frame)
{
	int ret;
	LC3BFI_t frame_status;
	uint16_t plc_counter = 0;

	if (audio_ch >= dec_num_instances) {
		LOG_ERR("Trying to use audio_ch outside of allowed range");
		return -EINVAL;
	}

	if (bad_frame) {
		frame_status = BadFrame;
	} else {
		frame_status = GoodFrame;
	}

	LC3DecodeInput_t LC3DecodeInput = { .inputData = (uint8_t *)lc3_data,
					    .inputDataLength = lc3_data_size,
					    frame_status };
	LC3DecodeOutput_t LC3DecodeOutput = { .PCMData = pcm_data,
					      .PCMDataLength = pcm_data_buf_size,
					      .bytesWritten = 0,
					      .PLCCounter = plc_counter };

	if (!dec_handle_ch[audio_ch]) {
		LOG_ERR("LC3 dec ch:%d is not initialized", audio_ch);
		return -EPERM;
	}
	ret = LC3DecodeSessionData(dec_handle_ch[audio_ch], &LC3DecodeInput, &LC3DecodeOutput);
	if (ret) {
		return ret;
	}

	/* A non-zero PLCCounter indicates PLC has been applied to the current frame.
	 * Upon a successful decode, PLCCounter is reset.
	 */
	if (LC3DecodeOutput.PLCCounter > 0 && IS_ENABLED(CONFIG_LC3_PLC_DISABLED))
	{
		/* Write a blank frame (all 0) into buffer. */
		memset(pcm_data, 0, pcm_data_buf_size);
	}

	*pcm_data_wr_size = LC3DecodeOutput.bytesWritten;

	return 0;
}

int sw_codec_lc3_enc_uninit_all(void)
{
	for (uint8_t i = 0; i < enc_num_instances; i++) {
		if (enc_handle_ch[i] != NULL) {
			LC3EncodeSessionClose(enc_handle_ch[i]);
			enc_handle_ch[i] = NULL;
		} else {
			LOG_ERR("Encoder:%d already unitialized", i);
			return -EALREADY;
		}
	}

	return 0;
}

int sw_codec_lc3_dec_uninit_all(void)
{
	for (uint8_t i = 0; i < dec_num_instances; i++) {
		if (dec_handle_ch[i] != NULL) {
			LC3DecodeSessionClose(dec_handle_ch[i]);
			dec_handle_ch[i] = NULL;
		} else {
			LOG_ERR("Decoder:%d already unitialized", i);
			return -EALREADY;
		}
	}

	return 0;
}

int sw_codec_lc3_init(uint8_t *sw_codec_lc3_buffer, uint32_t *sw_codec_lc3_buffer_size,
		      uint16_t framesize_us)
{
	int ret;
	uint8_t enc_sample_rates = 0;
	uint8_t dec_sample_rates = 0;

	/* Set unique session to 0 for using the default sharing memory setting.
	 *
	 * This could lead to higher heap consumption, but is able to manipulate
	 * different sample rate setting between encoder/decoder.
	 */
	uint8_t unique_session = 0;

	/* Check supported sample rates for encoder */
	if (IS_ENABLED(CONFIG_LC3_ENC_SAMPLE_RATE_8KHZ_SUPPORT)) {
		enc_sample_rates |= LC3_SAMPLE_RATE_8_KHZ;
	}
	if (IS_ENABLED(CONFIG_LC3_ENC_SAMPLE_RATE_16KHZ_SUPPORT)) {
		enc_sample_rates |= LC3_SAMPLE_RATE_16_KHZ;
	}
	if (IS_ENABLED(CONFIG_LC3_ENC_SAMPLE_RATE_24KHZ_SUPPORT)) {
		enc_sample_rates |= LC3_SAMPLE_RATE_24_KHZ;
	}
	if (IS_ENABLED(CONFIG_LC3_ENC_SAMPLE_RATE_32KHZ_SUPPORT)) {
		enc_sample_rates |= LC3_SAMPLE_RATE_32_KHZ;
	}
	if (IS_ENABLED(CONFIG_LC3_ENC_SAMPLE_RATE_441KHZ_SUPPORT)) {
		enc_sample_rates |= LC3_SAMPLE_RATE_441_KHZ;
	}
	if (IS_ENABLED(CONFIG_LC3_ENC_SAMPLE_RATE_48KHZ_SUPPORT)) {
		enc_sample_rates |= LC3_SAMPLE_RATE_48_KHZ;
	}

	/* Check supported sample rates for decoder */
	if (IS_ENABLED(CONFIG_LC3_DEC_SAMPLE_RATE_8KHZ_SUPPORT)) {
		dec_sample_rates |= LC3_SAMPLE_RATE_8_KHZ;
	}
	if (IS_ENABLED(CONFIG_LC3_DEC_SAMPLE_RATE_16KHZ_SUPPORT)) {
		dec_sample_rates |= LC3_SAMPLE_RATE_16_KHZ;
	}
	if (IS_ENABLED(CONFIG_LC3_DEC_SAMPLE_RATE_24KHZ_SUPPORT)) {
		dec_sample_rates |= LC3_SAMPLE_RATE_24_KHZ;
	}
	if (IS_ENABLED(CONFIG_LC3_DEC_SAMPLE_RATE_32KHZ_SUPPORT)) {
		dec_sample_rates |= LC3_SAMPLE_RATE_32_KHZ;
	}
	if (IS_ENABLED(CONFIG_LC3_DEC_SAMPLE_RATE_441KHZ_SUPPORT)) {
		dec_sample_rates |= LC3_SAMPLE_RATE_441_KHZ;
	}
	if (IS_ENABLED(CONFIG_LC3_DEC_SAMPLE_RATE_48KHZ_SUPPORT)) {
		dec_sample_rates |= LC3_SAMPLE_RATE_48_KHZ;
	}

	printk("sw_codec_lc3_init: enc_sample_rates:0x%x, dec_sample_rates:0x%x\r\n", enc_sample_rates, dec_sample_rates);

	LC3FrameSize_t framesize;

	switch (framesize_us) {
	case 7500:
		framesize = LC3FrameSize7_5Ms;
		break;
	case 10000:
		framesize = LC3FrameSize10Ms;
		break;
	default:
		LOG_ERR("Unsupported framesize: %d", framesize_us);
		return -EINVAL;
	}

	printk("sw_codec_lc3_init: framesize:%d\r\n", framesize);

	ret = LC3Initialize(enc_sample_rates, dec_sample_rates, framesize, unique_session,
			    sw_codec_lc3_buffer, sw_codec_lc3_buffer_size);

	printk("LC3Initialize: ret = %d\r\n", ret);
	return ret;
}

int sw_codec_lc3_enc_init(uint16_t pcm_sample_rate, uint8_t pcm_bit_depth, uint16_t framesize_us,
			  uint32_t enc_bitrate, uint8_t num_channels, uint16_t *const pcm_bytes_req)
{
	int ret;
	LC3FrameSize_t framesize;

	switch (framesize_us) {
	case 7500:
		framesize = LC3FrameSize7_5Ms;
		break;
	case 10000:
		framesize = LC3FrameSize10Ms;
		break;
	default:
		LOG_ERR("Unsupported framesize: %d", framesize_us);
		return -EINVAL;
	}

	if (enc_bitrate == 0) {
		LOG_ERR("LC3 enc_bitrate is 0");
		return -EINVAL;
	} else if (enc_bitrate <= ENC_BITRATE_WRN_LVL_LOW) {
		LOG_WRN("LC3 enc_bitrate: %d : likely too low", enc_bitrate);
	} else if (enc_bitrate >= ENC_BITRATE_WRN_LVL_HIGH) {
		LOG_WRN("LC3 enc_bitrate: %d : likely too high", enc_bitrate);
	}

	enc_pcm_bytes_req = LC3PCMBuffersize(pcm_sample_rate, pcm_bit_depth, framesize, &ret);
	*pcm_bytes_req = enc_pcm_bytes_req;

	if (enc_pcm_bytes_req == 0) {
		LOG_ERR("Required PCM bytes to encode LC3 is zero.");
		return -EPERM;
	}

	for (uint8_t i = 0; i < num_channels; i++) {
		if (enc_handle_ch[i]) {
			LOG_ERR("LC3 enc ch: %d already initialized", i);
			return -EALREADY;
		}
		enc_handle_ch[i] = LC3EncodeSessionOpen(pcm_sample_rate, pcm_bit_depth, framesize,
							NULL, NULL, &ret);
		if (ret) {
			return ret;
		}
	}

	m_enc_bitrate = enc_bitrate;

	enc_num_instances = num_channels;

	return ret;
}

int sw_codec_lc3_dec_init(uint16_t pcm_sample_rate, uint8_t pcm_bit_depth, uint16_t framesize_us,
			  uint8_t num_channels)
{
	int ret;

	LC3FrameSize_t framesize;

	switch (framesize_us) {
	case 7500:
		framesize = LC3FrameSize7_5Ms;
		break;
	case 10000:
		framesize = LC3FrameSize10Ms;
		break;
	default:
		LOG_ERR("Unsupported framesize: %d", framesize_us);
		return -EINVAL;
	}

	for (uint8_t i = 0; i < num_channels; i++) {
		if (dec_handle_ch[i]) {
			LOG_ERR("LC3 dec ch: %d already initialized", i);
			return -EALREADY;
		}
		dec_handle_ch[i] = LC3DecodeSessionOpen(pcm_sample_rate, pcm_bit_depth, framesize,
							NULL, NULL, &ret);
		if (ret) {
			return ret;
		}
	}

	dec_num_instances = num_channels;

	return 0;
}

I do see a similar post here LC3 initialization causes a restart but it was finally not resolved. So I have some questions for you as below:

1. So can you tell if there is any special memory reuqirement for LC3 code? Is it different from system heap memory? 


2. As you can see the first parmater(i.e. sw_codec_lc3_buffer) is passed in as NULL when calling sw_codec_lc3_init(NULL, NULL, CONFIG_AUDIO_FRAME_DURATION_US)
does that mean it will let LC3 lib allocate the memory dynamically on its own?

Why does it return -5002:LC3_RESULT_INSUFFICIENT_RESOURCES  as error code?

Parents
  • Hello,

    1. So can you tell if there is any special memory reuqirement for LC3 code? Is it different from system heap memory? 

    I am not sure, but it would be interesting to see if there are any obvious differences for the same application built for your custom board and the nRF5340 Audio DK. Are you willing to build both versions and share the resulting .config for the application core and sysbuild with us?

    There could be configurations which are invoked for the Audio DK but not for your custom board.

    2. As you can see the first parmater(i.e. sw_codec_lc3_buffer) is passed in as NULL when calling sw_codec_lc3_init(NULL, NULL, CONFIG_AUDIO_FRAME_DURATION_US)
    does that mean it will let LC3 lib allocate the memory dynamically on its own?

    According to the function description, the needed memory should be allocated. (ref https://github.com/nrfconnect/sdk-nrfxlib/blame/v2.9.0/lc3/codec/inc/LC3API.h#L213)

    Since it is not succeeding to do so, it is interesting to find out which value *sw_codec_lc3_buffer points to after the initialization function fails.

    I am not sure if the value is updated when the input parameters are NULL, but it will be updated if the bufferSize value is too small. Since LC3_RESULT_INSUFFICIENT_RESOURCES is returned it does indicate that the initialization function is called with a *bufferSize < "required memory for codec" at some point.

    As another trial to see if the LC3 codec will run on your hardware is to look into the sw_code_lc3 test sample: https://github.com/nrfconnect/sdk-nrf/blob/v2.9.0/tests/nrf5340_audio/sw_codec_lc3/main.c

    Best regards,

    Maria

Reply
  • Hello,

    1. So can you tell if there is any special memory reuqirement for LC3 code? Is it different from system heap memory? 

    I am not sure, but it would be interesting to see if there are any obvious differences for the same application built for your custom board and the nRF5340 Audio DK. Are you willing to build both versions and share the resulting .config for the application core and sysbuild with us?

    There could be configurations which are invoked for the Audio DK but not for your custom board.

    2. As you can see the first parmater(i.e. sw_codec_lc3_buffer) is passed in as NULL when calling sw_codec_lc3_init(NULL, NULL, CONFIG_AUDIO_FRAME_DURATION_US)
    does that mean it will let LC3 lib allocate the memory dynamically on its own?

    According to the function description, the needed memory should be allocated. (ref https://github.com/nrfconnect/sdk-nrfxlib/blame/v2.9.0/lc3/codec/inc/LC3API.h#L213)

    Since it is not succeeding to do so, it is interesting to find out which value *sw_codec_lc3_buffer points to after the initialization function fails.

    I am not sure if the value is updated when the input parameters are NULL, but it will be updated if the bufferSize value is too small. Since LC3_RESULT_INSUFFICIENT_RESOURCES is returned it does indicate that the initialization function is called with a *bufferSize < "required memory for codec" at some point.

    As another trial to see if the LC3 codec will run on your hardware is to look into the sw_code_lc3 test sample: https://github.com/nrfconnect/sdk-nrf/blob/v2.9.0/tests/nrf5340_audio/sw_codec_lc3/main.c

    Best regards,

    Maria

Children
No Data
Related