I2S Reading in Thread Throwing Hard Fault

Hi there,

So, this is in a sequence of attempts to read data from a digital microphone SPH0645 using i2s master protcool on nRF5340. The program layout is as follows:

I have written a thread for reading the i2s data that is enabled only after a semaphore is taken. Eventually I want to use this architecture integrated with a few other microcontroller operations. Here is the main.c file:

#include <zephyr/kernel.h>
#include <zephyr/sys/printk.h>
#include <zephyr/drivers/i2s.h>
#include <zephyr/drivers/gpio.h>
#include <zephyr/logging/log.h>

LOG_MODULE_REGISTER(main, LOG_LEVEL_INF);

#define SAMPLE_FREQUENCY 16000
#define SAMPLE_BIT_WIDTH 32
#define BYTES_PER_SAMPLE sizeof(int32_t)
#define NUMBER_OF_CHANNELS 1
#define SAMPLES_PER_BLOCK ((SAMPLE_FREQUENCY / 10) * NUMBER_OF_CHANNELS)
#define BLOCK_SIZE (BYTES_PER_SAMPLE * SAMPLES_PER_BLOCK)
#define BLOCK_COUNT 20
#define RECORD_DURATION_MS 3000

K_MEM_SLAB_DEFINE_STATIC(mem_slab, BLOCK_SIZE, BLOCK_COUNT, 4);
K_SEM_DEFINE(enable_recording, 0, 1);

K_THREAD_STACK_DEFINE(recording_thread_stack, 4096);
struct k_thread recording_thread_data;
static const struct device *mic;
static bool recording_active = false;

void start_recording() {
    if (!recording_active) {
        recording_active = true;
        LOG_INF("Starting recording");

        int ret = i2s_trigger(mic, I2S_DIR_RX, I2S_TRIGGER_START);
        if (ret) {
            LOG_ERR("Failed to start I2S recording: %d", ret);
            recording_active = false;
        } else {
            LOG_INF("Ready for recording. Press Button when ready.\n");
            k_sem_give(&enable_recording);
        }
    }
}

void stop_recording() {
    if (recording_active) {
        LOG_INF("Stopping recording");
        recording_active = false;

        int ret = i2s_trigger(mic, I2S_DIR_RX, I2S_TRIGGER_STOP);
        if (ret) {
            LOG_ERR("Failed to stop I2S recording: %d", ret);
        }
    }
}
void mic_worker_thread(void *p1, void *p2, void *p3) {
    LOG_INF("Worker thread started");

    while (1) {
        // Wait for the semaphore to be given
        k_sem_take(&enable_recording, K_FOREVER);
        
        start_recording();

        void *mem_block;
        size_t data_size;
        int64_t start_time = k_uptime_get();

        while (k_uptime_get() - start_time < RECORD_DURATION_MS) {
            data_size = 0;

            int ret = i2s_read(mic, &mem_block, &data_size);

            if (ret < 0) {
                LOG_ERR("Failed to read data: %d", ret);
                break;
            }

            if (data_size > 0) {
                LOG_INF("Data size: %zu bytes", data_size);
                LOG_INF("Elapsed time: %lld ms", k_uptime_get() - start_time);

                // Free the memory block after processing
                k_mem_slab_free(&mem_slab, &mem_block);
                LOG_DBG("Block of data freed.\n");
            }
        }

        recording_active = false;
        stop_recording();

        // Semaphore should only be given once the recording session is fully complete
        k_sem_give(&enable_recording);
    }
}

void recording_init() {
    mic = DEVICE_DT_GET(DT_NODELABEL(i2s0));
    if (!device_is_ready(mic)) {
        LOG_ERR("I2S device not ready");
        return;
    }

    struct i2s_config config = {
        .word_size = SAMPLE_BIT_WIDTH,
        .channels = NUMBER_OF_CHANNELS,
        .format = I2S_FMT_DATA_FORMAT_I2S,
        .options = I2S_OPT_BIT_CLK_MASTER | I2S_OPT_FRAME_CLK_MASTER,
        .frame_clk_freq = SAMPLE_FREQUENCY,
        .mem_slab = &mem_slab,
        .block_size = BLOCK_SIZE,
        .timeout = 1000,
    };
    LOG_INF("Configured I2S device");

    if (i2s_configure(mic, I2S_DIR_RX, &config) != 0) {
        LOG_ERR("Failed to configure I2S");
    }

    k_thread_create(
        &recording_thread_data,
        recording_thread_stack,
        K_THREAD_STACK_SIZEOF(recording_thread_stack),
        mic_worker_thread,   // Thread function
        NULL, NULL, NULL,    // Arguments to the thread
        5, 0, K_NO_WAIT      // Priority, options, and start time
    );
}


int main(void) {
    LOG_INF("Starting main thread");

    recording_init();

    

    // while (1) {
    k_sem_give(&enable_recording);
    // start_recording();
        k_sleep(K_SECONDS(5));
    // }
    // k_sem_take(&enable_recording, K_FOREVER);

    return 0;
}

Here is the overlay file to point out pin connections:

/*
 * Copyright (c) 2021 Nordic Semiconductor ASA
 *
 * SPDX-License-Identifier: Apache-2.0
 */
 &clock {
	// hfclk-source = "HFXO"; /* Select the High-Frequency External Oscillator */
	status = "okay";
};


&pinctrl {
	
	
	i2s0_default_alt: i2s0_default_alt {
		group1 {
			psels = <NRF_PSEL(I2S_SCK_M, 0, 26)>,
				<NRF_PSEL(I2S_LRCK_M, 0, 7)>,
				<NRF_PSEL(I2S_SDOUT, 1, 13)>,
				<NRF_PSEL(I2S_SDIN, 0, 25)>;
		};
	};
};


&clock {
	hfclkaudio-frequency = <11289600>;
};

i2s_rxtx: &i2s0 {
	status = "okay";
	pinctrl-0 = <&i2s0_default_alt>;
	pinctrl-names = "default";
	clock-source = "ACLK";
};

Here are my configuration settings for the project:

# CONFIG_STDOUT_CONSOLE=y
CONFIG_PRINTK=y
CONFIG_PWM=y

CONFIG_SERIAL=y
CONFIG_UART_INTERRUPT_DRIVEN=y
CONFIG_I2S=y
CONFIG_GPIO=y
CONFIG_I2S_NRFX_RX_BLOCK_COUNT=10
# CONFIG_LOG_TIMESTAMP=n

CONFIG_LOG=y
# CONFIG_LOG_DEFAULT_LEVEL=4
CONFIG_LOG_PRINTK=y
CONFIG_LOG_MODE_IMMEDIATE=y
CONFIG_PWM_LOG_LEVEL_DBG=y
CONFIG_LOG_BUFFER_SIZE=4096
CONFIG_LOG_PROCESS_THREAD=y
CONFIG_LOG_PROCESS_THREAD_STACK_SIZE=4096
# CONFIG_LOG_BACKEND_FORMAT_TIMESTAMP=n

But as I run the application, I see a hard fault occurring after four samples are received:

[00:00:00.266,723] <27>[0m<inf> main: Configured I2S device<27>[0m<\r><\n>
[00:00:00.272,125] <27>[0m<inf> i2s_nrfx: I2S MCK frequency: 1026327, actual PCM rate: 16036<27>[0m<\r><\n>
[00:00:00.280,334] <27>[0m<inf> main: Worker thread started<27>[0m<\r><\n>
[00:00:00.285,675] <27>[0m<inf> main: Starting recording<27>[0m<\r><\n>
[00:[00:00:00.291,168] <27>[0m<inf> i2s_nrfx: Next buffers: 0/0x2001ee98<27>[0m<\r><\n>
00:00.290,802] <27>[0m<inf> main: Ready for recording. Press Button when ready.<\n><27>[0m<\r><\n>
[00:00:00.390,960] <27>[0m<inf> i2s_nrfx: Queued RX 0x20020798<27>[0m<\r><\n>
[00:00:00.396,514] <27>[0m<inf> i2s_nrfx: Next buffers: 0/0x2001d598<27>[0m<\r><\n>
[00:00:00.402,648] <27>[0m<inf> i2s_nrfx: Released RX 0x20020798<27>[0m<\r><\n>
[00:00:00.408,416] <27>[0m<inf> main: Data size: 6400 bytes<27>[0m<\r><\n>
[00:00:00.413,787] <27>[0m<inf> main: Elapsed time: 109 ms<27>[0m<\r><\n>
[00:00:00.490,722] <27>[0m<inf> i2s_nrfx: Queued RX 0x2001ee98<27>[0m<\r><\n>
[00:00:00.496,276] <27>[0m<inf> i2s_nrfx: Next buffers: 0/0x20001f18<27>[0m<\r><\n>
[00:00:00.502,410] <27>[0m<inf> i2s_nrfx: Released RX 0x2001ee98<27>[0m<\r><\n>
[00:00:00.508,209] <27>[0m<inf> main: Data size: 6400 bytes<27>[0m<\r><\n>
[00:00:00.513,580] <27>[0m<inf> main: Elapsed time: 209 ms<27>[0m<\r><\n>
[00:00:00.590,484] <27>[0m<inf> i2s_nrfx: Queued RX 0x2001d598<27>[0m<\r><\n>
[00:00:00.596,069] <27>[0m<inf> i2s_nrfx: Next buffers: 0/0x20001f18<27>[0m<\r><\n>
[00:00:00.602,203] <27>[0m<inf> i2s_nrfx: Released RX 0x2001d598<27>[0m<\r><\n>
[00:00:00.607,971] <27>[0m<inf> main: Data size: 6400 bytes<27>[0m<\r><\n>
[00:00:00.613,342] <27>[0m<inf> main: Elapsed time: 309 ms<27>[0m<\r><\n>
[00:00:00.690,277] <27>[0m<inf> i2s_nrfx: Queued RX 0x20001f18<27>[0m<\r><\n>
[00:00:00.695,831] <27>[0m<inf> i2s_nrfx: Next buffers: 0/0x20001f18<27>[0m<\r><\n>
[00:00:00.701,965] <27>[0m<inf> i2s_nrfx: Released RX 0x20001f18<27>[0m<\r><\n>
[00:00:00.707,763] <27>[0m<inf> main: Data size: 6400 bytes<27>[0m<\r><\n>
[00:00:00.713,104] <27>[0m<inf> main: Elapsed time: 409 ms<27>[0m<\r><\n>
[00:00:00.718,414] <27>[1;31m<err> os: [00:00:00.721,496] <27>[1;31m<err> os: ***** HARD FAULT *****<27>[0m<\r><\n>
[00:00:00.727,020] <27>[1;31m<err> os:   Fault escalation (see below)<27>[0m<\r><\n>
[00:00:00.733,245] <27>[1;31m<err> os: ***** MPU FAULT *****<27>[0m<\r><\n>
[00:00:00.738,708] <27>[1;31m<err> os:   Instruction Access Violation<27>[0m<\r><\n>
[00:00:00.744,934] <27>[1;31m<err> os: r0/a1:  0x200025a0  r1/a2:  0xfb620000  r2/a3:  0x80000000<27>[0m<\r><\n>
[00:00:00.753,570] <27>[1;31m<err> os: r3/a4:  0x00000002 r12/ip:  0x00002425 r14/lr:  0x00002453<27>[0m<\r><\n>
[00:00:00.762,207] <27>[1;31m<err> os:  xpsr:  0x41000004<27>[0m<\r><\n>
[00:00:00.767,395] <27>[1;31m<err> os: Faulting instruction address (r15/pc): 0x000071d0<27>[0m<\r><\n>
[00:00:00.775,268] <27>[1;31m<err> os: >>> ZEPHYR FATAL ERROR 20: Unknown error on CPU 0<27>[0m<\r><\n>
[00:00:00.783,142] <27>[1;31m<err> os: Fault during interrupt handling<\n><27>[0m<\r><\n>
[00:00:00.789,520] <27>[1;31m<err> os: Current thread: 0x200003a0 (unknown)<27>[0m<\r><\n>
[00:00:00.796,264] <27>[1;31m<err> os: Halting system<27>[0m

I have not been able to trace back the instruction from the provided address: 0x000071d0. The command 'addrline.exe' gives me '????'. I am freeing the memory as soon as the i2s_read happens. 

I have tried extending the thread call stack but to no avail. 

Please advise what can be done. 

Related