diff --git a/doc/exclude_patterns.inc b/doc/exclude_patterns.inc index 9ddf9cb8..f5a5bea5 100644 --- a/doc/exclude_patterns.inc +++ b/doc/exclude_patterns.inc @@ -7,4 +7,4 @@ LICENSE.rst build.xcore tests/**/.pytest_cache/*.md tests/.pytest_cache/*.md -**/app_mic_array_basic/*.md +**/app_mic_array_basic*/*.md diff --git a/examples/app_mic_array_basic_xcore_400/CMakeLists.txt b/examples/app_mic_array_basic_xcore_400/CMakeLists.txt new file mode 100644 index 00000000..1956c441 --- /dev/null +++ b/examples/app_mic_array_basic_xcore_400/CMakeLists.txt @@ -0,0 +1,24 @@ +cmake_minimum_required(VERSION 3.21) +include($ENV{XMOS_CMAKE_PATH}/xcommon.cmake) +project(app_mic_array) + +set(XMOS_SANDBOX_DIR ${CMAKE_CURRENT_LIST_DIR}/../../..) + +set(APP_HW_TARGET XK-EVK-XU416) +set(APP_DEPENDENT_MODULES "lib_mic_array") +set(APP_INCLUDES src) + +set(APP_COMPILER_FLAGS + -Os + -g + -report + -Wall + -fxscope + # -Wno-xcore-fptrgroup + # Mic array config + -DMIC_ARRAY_CONFIG_SAMPLES_PER_FRAME=320 + -DMIC_ARRAY_CONFIG_MIC_COUNT=1 + -DMIC_ARRAY_CONFIG_USE_PDM_ISR=0 +) + +XMOS_REGISTER_APP() diff --git a/examples/app_mic_array_basic_xcore_400/README.md b/examples/app_mic_array_basic_xcore_400/README.md new file mode 100644 index 00000000..3c305b91 --- /dev/null +++ b/examples/app_mic_array_basic_xcore_400/README.md @@ -0,0 +1,30 @@ +# Basic Mic Array Example + +## Hardware Required + +- **XMS0016** + +## Compile + +```sh +cmake -G "Unix Makefiles" -B build +xmake -C build +``` + +## Run + +```sh +xrun --xscope bin/app_mic_array.xe +``` + +## Convert Binary Data to WAV + +```sh +python convert.py +``` + +**Output:** + +``` +Converted mic_array_output.bin to output.wav with 1 channels, 16000 Hz sample rate, and 32 bits per sample. +``` diff --git a/examples/app_mic_array_basic_xcore_400/convert.py b/examples/app_mic_array_basic_xcore_400/convert.py new file mode 100644 index 00000000..5dbf499d --- /dev/null +++ b/examples/app_mic_array_basic_xcore_400/convert.py @@ -0,0 +1,27 @@ +# Copyright 2026 XMOS LIMITED. +# This Software is subject to the terms of the XMOS Public Licence: Version 1. + +import numpy as np +import wave +import soundfile as sf + + +def convert_to_wav( + input_file, output_file, num_channels=1, sample_rate=16000, bits_per_sample=32 +): + with open(input_file, "rb") as inp_f: + data = inp_f.read() + data = np.frombuffer(data, dtype=np.int32) + + sf.write(output_file, data, sample_rate, subtype='PCM_32') + print(f"Converted {input_file} to {output_file} with {num_channels} channels, {sample_rate} Hz sample rate, and {bits_per_sample} bits per sample.") + + +if __name__ == "__main__": + convert_to_wav( + input_file="mic_array_output.bin", + output_file="output.wav", + num_channels=1, + sample_rate=16000, + bits_per_sample=32 + ) diff --git a/examples/app_mic_array_basic_xcore_400/mic_array_output.bin b/examples/app_mic_array_basic_xcore_400/mic_array_output.bin new file mode 100644 index 00000000..4f69a22c Binary files /dev/null and b/examples/app_mic_array_basic_xcore_400/mic_array_output.bin differ diff --git a/examples/app_mic_array_basic_xcore_400/output.wav b/examples/app_mic_array_basic_xcore_400/output.wav new file mode 100644 index 00000000..dc809209 Binary files /dev/null and b/examples/app_mic_array_basic_xcore_400/output.wav differ diff --git a/examples/app_mic_array_basic_xcore_400/src/app.c b/examples/app_mic_array_basic_xcore_400/src/app.c new file mode 100644 index 00000000..831d29ee --- /dev/null +++ b/examples/app_mic_array_basic_xcore_400/src/app.c @@ -0,0 +1,99 @@ +// Copyright 2026 XMOS LIMITED. +// This Software is subject to the terms of the XMOS Public Licence: Version 1. +#include +#include +#include + +#if defined (__XS3A__) +#include +#elif defined (__VX4B__) +#include +#endif + +#include +#include +#include + +#include "mic_array.h" +#include "device_pll_ctrl.h" + +// -------------------- Frecuency and Port definitions -------------------- +#define MIC_ARRAY_CONFIG_MCLK_FREQ (24576000) +#define MIC_ARRAY_CONFIG_PDM_FREQ (3072000) +#define MIC_ARRAY_CONFIG_PORT_MCLK XS1_PORT_1D +#define MIC_ARRAY_CONFIG_PORT_PDM_CLK XS1_PORT_1G /* X1D22 */ +#define MIC_ARRAY_CONFIG_PORT_PDM_DATA XS1_PORT_1F /* X1D13 */ +#define MIC_ARRAY_CONFIG_CLOCK_BLOCK_A XS1_CLKBLK_2 +// ------------------------------------------------------------ + +// App defines +#define APP_N_SAMPLES (320) +#define APP_OUT_FREQ_HZ (16000) +#define APP_SAMPLE_SECONDS (2) +#define APP_N_FRAMES (APP_OUT_FREQ_HZ * APP_SAMPLE_SECONDS / APP_N_SAMPLES) +#define APP_BUFF_SIZE (APP_N_FRAMES * APP_N_SAMPLES) +#define APP_FILENAME ("mic_array_output.bin") + +DECLARE_JOB(user_mic, (chanend_t)); +DECLARE_JOB(user_audio, (chanend_t)); + +static pdm_rx_resources_t pdm_res = PDM_RX_RESOURCES_SDR( + MIC_ARRAY_CONFIG_PORT_MCLK, + MIC_ARRAY_CONFIG_PORT_PDM_CLK, + MIC_ARRAY_CONFIG_PORT_PDM_DATA, + MIC_ARRAY_CONFIG_MCLK_FREQ, + MIC_ARRAY_CONFIG_PDM_FREQ, + MIC_ARRAY_CONFIG_CLOCK_BLOCK_A); + +void user_mic(chanend_t c_mic_audio) +{ + printf("Mic Init\n"); + device_pll_init(); + mic_array_init(&pdm_res, NULL, APP_OUT_FREQ_HZ); + mic_array_start(c_mic_audio); +} + +void user_audio(chanend_t c_mic_audio) +{ + int32_t WORD_ALIGNED tmp_buff[APP_BUFF_SIZE] = {0}; + int32_t *buff_ptr = &tmp_buff[0]; + unsigned frame_counter = APP_N_FRAMES; + while (frame_counter--) + { + ma_frame_rx(buff_ptr, (chanend_t)c_mic_audio, MIC_ARRAY_CONFIG_MIC_COUNT, APP_N_SAMPLES); + buff_ptr += APP_N_SAMPLES; + for (unsigned i = 0; i < APP_N_SAMPLES; i++) + { + tmp_buff[i] <<= 6; + } + } + + // write samples to a binary file + printf("Writing output to %s\n", APP_FILENAME); + FILE *f = fopen(APP_FILENAME, "wb"); + assert(f != NULL); + fwrite(tmp_buff, sizeof(int32_t), APP_BUFF_SIZE, f); + fclose(f); + ma_shutdown(c_mic_audio); + printf("Done\n"); +} + +void main_tile_1(){ + channel_t c_mic_audio = chan_alloc(); + // Parallel Jobs + PAR_JOBS( + PJOB(user_mic, (c_mic_audio.end_a)), + PJOB(user_audio, (c_mic_audio.end_b)) + ); + chan_free(c_mic_audio); +} + +void main_tile_0(){ + // intentionally left empty + return; +} + +NETWORK_MAIN( + TILE_MAIN(main_tile_1, 1, ()), + TILE_MAIN(main_tile_0, 0, ()) +) diff --git a/examples/app_mic_array_basic_xcore_400/src/config.xscope b/examples/app_mic_array_basic_xcore_400/src/config.xscope new file mode 100644 index 00000000..d3a3da63 --- /dev/null +++ b/examples/app_mic_array_basic_xcore_400/src/config.xscope @@ -0,0 +1,18 @@ + + + + + + + + + + + + + + + + + + diff --git a/examples/app_mic_array_basic_xcore_400/src/device_pll_ctrl.c b/examples/app_mic_array_basic_xcore_400/src/device_pll_ctrl.c new file mode 100644 index 00000000..233e2911 --- /dev/null +++ b/examples/app_mic_array_basic_xcore_400/src/device_pll_ctrl.c @@ -0,0 +1,73 @@ +// Copyright 2022-2026 XMOS LIMITED. +// This Software is subject to the terms of the XMOS Public Licence: Version 1. + +#if defined (__XS3A__) +#include +#include +#elif defined (__VX4B__) +#include +#include +#include +#endif + +#include +#include +#include "device_pll_ctrl.h" + + +void device_pll_init(void) +{ + #if 0 + unsigned tileid = get_local_tile_id(); + + #if defined (__XS3A__) + // SS_APP_PLL_BYPASS (29:29) = 0b0 = 0x0 + // SS_APP_PLL_INPUT_FROM_SYS_PLL (28:28) = 0b0 = 0x0 + // SS_APP_PLL_ENABLE (27:27) = 0b0 = 0x0 + // Reserved (26:26) = 0b0 = 0x0 + // SS_PLL_CTL_POST_DIVISOR (25:23) = 0b100 = 0x4 + // Reserved (22:21) = 0b00 = 0x0 + // SS_PLL_CTL_FEEDBACK_MUL (20:08) = 0b0000111111111 = 0x01FF + // Reserved (07:06) = 0b00 = 0x0 + // SS_PLL_CTL_INPUT_DIVISOR (05:00) = 0b000100 = 0x04 + const unsigned DEVICE_PLL_DISABLE = 0x0201FF04; + + // SS_APP_CLK_FROM_APP_PLL (31:31) = 0b1 = 0x1 + // Reserved (30:17) = 0b00000000000000 = 0x0 + // SS_APP_CLK_DIV_DISABLE (16:16) = 0b0 = 0x0 + // SS_APP_CLK_DIV (15:00) = 0b0000000000000100 = 0x0004 + const unsigned DEVICE_PLL_DIV_0 = 0x80000004; + #elif defined (__VX4B__) + // Reserved (31:31) = 0b0 = 0x0 + // NLOCK (30:30) = 0b0 = 0x0 + // Reserved (29:29) = 0b0 = 0x0 + // BYPASS (28:28) = 0b0 = 0x0 + // Reserved (27:27) = 0b0 = 0x0 + // DISABLE (26:26) = 0b1 = 0x1 + // OD_DIVIDER (25:23) = 0b100 = 0x4 + // Reserved (22:21) = 0b00 = 0x0 + // F_MULTIPLIER (20:08) = 0b0000111111111 = 0x01FF + // Reserved (07:06) = 0b00 = 0x0 + // R_DIVIDER (05:00) = 0b000100 = 0x04 + const unsigned DEVICE_PLL_DISABLE = 0x0601FF04; + const unsigned DEVICE_PLL_DIV_0 = 0x80000004; + #else + #error "Unsupported architecture" + #endif + + write_sswitch_reg(tileid, XS1_SSWITCH_SS_APP_PLL_CTL_NUM, + DEVICE_PLL_DISABLE); + + hwtimer_t tmr = hwtimer_alloc(); + { + xassert(tmr != 0); + hwtimer_delay(tmr, 100000); // 1ms with 100 MHz timer tick + } + hwtimer_free(tmr); + + write_sswitch_reg(tileid, XS1_SSWITCH_SS_APP_PLL_CTL_NUM, DEVICE_PLL_CTL_VAL); + write_sswitch_reg(tileid, XS1_SSWITCH_SS_APP_PLL_CTL_NUM, DEVICE_PLL_CTL_VAL); + write_sswitch_reg(tileid, XS1_SSWITCH_SS_APP_PLL_FRAC_N_DIVIDER_NUM, DEVICE_PLL_FRAC_NOM); + write_sswitch_reg(tileid, XS1_SSWITCH_SS_APP_CLK_DIVIDER_NUM, DEVICE_PLL_DIV_0); +#endif +} diff --git a/examples/app_mic_array_basic_xcore_400/src/device_pll_ctrl.h b/examples/app_mic_array_basic_xcore_400/src/device_pll_ctrl.h new file mode 100644 index 00000000..7fe002e6 --- /dev/null +++ b/examples/app_mic_array_basic_xcore_400/src/device_pll_ctrl.h @@ -0,0 +1,9 @@ +// Copyright 2022-2026 XMOS LIMITED. +// This Software is subject to the terms of the XMOS Public Licence: Version 1. + +#pragma once + +#define DEVICE_PLL_CTL_VAL 0x0A019803 // Valid for all fractional values +#define DEVICE_PLL_FRAC_NOM 0x800095F9 // 24.576000 MHz + +void device_pll_init(void); diff --git a/lib_mic_array/src/mic_array_task.c b/lib_mic_array/src/mic_array_task.c new file mode 100644 index 00000000..11009e73 --- /dev/null +++ b/lib_mic_array/src/mic_array_task.c @@ -0,0 +1,92 @@ +// Copyright 2026 XMOS LIMITED. +// This Software is subject to the terms of the XMOS Public Licence: Version 1. + +#include +#include +#include +#include + +#include "mic_array.h" +#include "mic_array_task_internal.hpp" + +//////////////////// +// Mic array init // +//////////////////// +void mic_array_init(pdm_rx_resources_t *pdm_res, const unsigned *channel_map, unsigned output_samp_freq) +{ + unsigned stg2_decimation_factor = (pdm_res->pdm_freq/STAGE1_DEC_FACTOR)/output_samp_freq; + assert ((output_samp_freq*STAGE1_DEC_FACTOR*stg2_decimation_factor) == pdm_res->pdm_freq); // assert if it doesn't divide cleanly + // assert if unsupported decimation factor. (for example. when starting with a pdm_freq of 3.072MHz, supported + // output sampling freqs are [48000, 32000, 16000] + assert ((stg2_decimation_factor == 2) || (stg2_decimation_factor == 3) || (stg2_decimation_factor == 6)); + + bool use_3_stg_decimator = false; + init_mic_array_storage(use_3_stg_decimator); + init_mics_default_filter(pdm_res, channel_map, stg2_decimation_factor); +} + +void mic_array_init_custom_filter(pdm_rx_resources_t* pdm_res, mic_array_conf_t* mic_array_conf) +{ + assert(pdm_res); + assert(mic_array_conf); + assert(mic_array_conf->decimator_conf.num_filter_stages == 2 || + mic_array_conf->decimator_conf.num_filter_stages == 3); + + init_mic_array_storage(mic_array_conf->decimator_conf.num_filter_stages == 3); + init_mics_custom_filter(pdm_res, mic_array_conf); + + // Configure and start clocks + const unsigned divide = pdm_res->mclk_freq / pdm_res->pdm_freq; + mic_array_resources_configure(pdm_res, divide); + mic_array_pdm_clock_start(pdm_res); +} + +///////////////////// +// Mic array start // +///////////////////// + +// Parallel jobs for when XUA_PDM_MIC_USE_PDM_ISR == 0, run separate decimator and pdm rx tasks +DECLARE_JOB(default_ma_task_start_pdm, (void)); +void default_ma_task_start_pdm(void) +{ + start_pdm_task(); +} + +DECLARE_JOB(default_ma_task_start_decimator, (void)); +void default_ma_task_start_decimator(void) +{ + start_decimator_task(); +} + +DECLARE_JOB(default_ma_task_start_pdm_3stg, (void)); +void default_ma_task_start_pdm_3stg(void) +{ + start_pdm_task_3stg(); +} + +DECLARE_JOB(default_ma_task_start_decimator_3stg, (void)); +void default_ma_task_start_decimator_3stg(void) +{ + start_decimator_task_3stg(); +} + +void mic_array_start(chanend_t c_frames_out) +{ +#if MIC_ARRAY_CONFIG_USE_PDM_ISR + start_mic_array_pdm_isr(c_frames_out); +#else + set_output_channel(c_frames_out); + bool use_3_stg_decimator = get_decimator_stg_count(); + if (use_3_stg_decimator) { + PAR_JOBS( + PJOB(default_ma_task_start_pdm_3stg, ()), + PJOB(default_ma_task_start_decimator_3stg, ())); + } else { + PAR_JOBS( + PJOB(default_ma_task_start_pdm, ()), + PJOB(default_ma_task_start_decimator, ())); + } +#endif // MIC_ARRAY_CONFIG_USE_PDM_ISR + + shutdown_mic_array(); +} diff --git a/lib_mic_array/src/mic_array_task.cpp b/lib_mic_array/src/mic_array_task.cpp index d5e5faab..718e1089 100644 --- a/lib_mic_array/src/mic_array_task.cpp +++ b/lib_mic_array/src/mic_array_task.cpp @@ -4,7 +4,6 @@ #include #include #include -#include #include #include @@ -12,40 +11,86 @@ #include "mic_array/etc/filters_default.h" #include "mic_array_task_internal.hpp" -TMicArray *g_mics = nullptr; // Global mic array instance. -TMicArray_3stg_decimator *g_mics_3stg = nullptr; -bool use_3_stg_decimator = false; -// NOTE: g_mics must persist (remain non-null and its backing storage valid) +static TMicArray *s_mics = nullptr; +static TMicArray_3stg_decimator *s_mics_3stg = nullptr; +static bool s_use_3_stg_decimator = false; +// NOTE: s_mics or s_mics_3stg must persist (remain non-null with its backing storage valid) // until mic_array_start() completes. mic_array_start() performs shutdown and -// then sets g_mics back to nullptr. +// then sets s_mics or s_mics_3stg back to nullptr. + +#if !defined (__XS2A__) +///////////////////////////// +// Static variable getters // +///////////////////////////// +bool get_decimator_stg_count(void) +{ + return s_use_3_stg_decimator; +} -#if !defined(__XS2A__) //////////////////// // Mic array init // //////////////////// -void mic_array_init(pdm_rx_resources_t *pdm_res, const unsigned *channel_map, unsigned output_samp_freq) +void init_mics_default_filter(pdm_rx_resources_t* pdm_res, const unsigned* channel_map, unsigned stg2_dec_factor) { - assert(g_mics == nullptr); // Mic array instance already initialised + static int32_t stg1_filter_state[MIC_ARRAY_CONFIG_MIC_COUNT][8]; + mic_array_decimator_conf_t decimator_conf; + memset(&decimator_conf, 0, sizeof(decimator_conf)); + mic_array_filter_conf_t filter_conf[2] = {{0}}; + + // decimator + decimator_conf.filter_conf = &filter_conf[0]; + decimator_conf.num_filter_stages = 2; + //filter stage 1 + filter_conf[0].coef = (int32_t*)stage_1_filter(stg2_dec_factor); + filter_conf[0].num_taps = 256; + filter_conf[0].decimation_factor = 32; + filter_conf[0].shr = 0; + filter_conf[0].state_words_per_channel = filter_conf[0].num_taps/32; + filter_conf[0].state = (int32_t*)stg1_filter_state; + + // filter stage 2 + filter_conf[1].coef = (int32_t*)stage_2_filter(stg2_dec_factor); + filter_conf[1].num_taps = stage_2_num_taps(stg2_dec_factor); + filter_conf[1].decimation_factor = stg2_dec_factor; + filter_conf[1].shr = stage_2_shift(stg2_dec_factor); + filter_conf[1].state_words_per_channel = decimator_conf.filter_conf[1].num_taps; + filter_conf[1].state = stage_2_state_memory(stg2_dec_factor); + + s_mics->Decimator.Init(decimator_conf); + + pdm_rx_conf_t pdm_rx_config; + pdm_rx_config.pdm_out_words_per_channel = stg2_dec_factor; + pdm_rx_config.pdm_out_block = get_pdm_rx_out_block(stg2_dec_factor); + pdm_rx_config.pdm_in_double_buf = get_pdm_rx_out_block_double_buf(stg2_dec_factor); + + s_mics->PdmRx.Init(pdm_res->p_pdm_mics, pdm_rx_config); + + if(channel_map) { + s_mics->PdmRx.MapChannels(channel_map); + } - use_3_stg_decimator = false; + int divide = pdm_res->mclk_freq / pdm_res->pdm_freq; + mic_array_resources_configure(pdm_res, divide); + mic_array_pdm_clock_start(pdm_res); +} - unsigned stg2_decimation_factor = (pdm_res->pdm_freq/STAGE1_DEC_FACTOR)/output_samp_freq; - assert ((output_samp_freq*STAGE1_DEC_FACTOR*stg2_decimation_factor) == pdm_res->pdm_freq); // assert if it doesn't divide cleanly - // assert if unsupported decimation factor. (for example. when starting with a pdm_freq of 3.072MHz, supported - // output sampling freqs are [48000, 32000, 16000] - assert ((stg2_decimation_factor == 2) || (stg2_decimation_factor == 3) || (stg2_decimation_factor == 6)); - static uint8_t __attribute__((aligned(8))) mic_storage[sizeof(TMicArray)]; - g_mics = new (mic_storage) TMicArray(); - init_mics_default_filter(g_mics, pdm_res, channel_map, stg2_decimation_factor); +void init_mic_array_storage(bool use_3_stg_decimator) +{ + assert(s_mics == nullptr && s_mics_3stg == nullptr); // Mic array instance already initialised + s_use_3_stg_decimator = use_3_stg_decimator; + if(s_use_3_stg_decimator) { + static uint8_t __attribute__((aligned(8))) mic_storage[sizeof(TMicArray_3stg_decimator)]; + s_mics_3stg = new (mic_storage) TMicArray_3stg_decimator(); + } else { + static uint8_t __attribute__((aligned(8))) mic_storage[sizeof(TMicArray)]; + s_mics = new (mic_storage) TMicArray(); + } } template -static inline void init_from_conf(TMics*& mics_ptr, - uint8_t* storage, - pdm_rx_resources_t* pdm_res, - mic_array_conf_t* conf) { - mics_ptr = new (storage) TMics(); +static inline void init_from_conf(TMics*& mics_ptr, pdm_rx_resources_t* pdm_res, mic_array_conf_t* conf) +{ mics_ptr->Decimator.Init(conf->decimator_conf); mics_ptr->PdmRx.Init(pdm_res->p_pdm_mics, conf->pdmrx_conf); if (conf->pdmrx_conf.channel_map) { @@ -54,67 +99,64 @@ static inline void init_from_conf(TMics*& mics_ptr, mics_ptr->PdmRx.AssertOnDroppedBlock(false); } -void mic_array_init_custom_filter(pdm_rx_resources_t* pdm_res, - mic_array_conf_t* mic_array_conf) +void init_mics_custom_filter(pdm_rx_resources_t* pdm_res, mic_array_conf_t* mic_array_conf) { - assert(pdm_res); - assert(mic_array_conf); - assert(g_mics == nullptr && g_mics_3stg == nullptr); - static uint8_t __attribute__((aligned(8))) mic_storage[sizeof(UAnyMicArray)]; - - if(mic_array_conf->decimator_conf.num_filter_stages == 2) - { - use_3_stg_decimator = false; - init_from_conf(g_mics, mic_storage, pdm_res, mic_array_conf); - } - else if(mic_array_conf->decimator_conf.num_filter_stages == 3) - { - init_from_conf(g_mics_3stg, mic_storage, pdm_res, mic_array_conf); - use_3_stg_decimator = true; + if(mic_array_conf->decimator_conf.num_filter_stages == 2) { + init_from_conf(s_mics, pdm_res, mic_array_conf); + } else if(mic_array_conf->decimator_conf.num_filter_stages == 3) { + init_from_conf(s_mics_3stg, pdm_res, mic_array_conf); + } else { + assert(false && "Unsupported number of filter stages in mic_array_conf"); } - // Configure and start clocks - const unsigned divide = pdm_res->mclk_freq / pdm_res->pdm_freq; - mic_array_resources_configure(pdm_res, divide); - mic_array_pdm_clock_start(pdm_res); } ///////////////////// // Mic array start // ///////////////////// - -// Parallel jobs for when XUA_PDM_MIC_USE_PDM_ISR == 0, run separate decimator and pdm rx tasks -DECLARE_JOB(default_ma_task_start_pdm, (TMicArray&)); -void default_ma_task_start_pdm(TMicArray& mics){ - mics.PdmRx.ThreadEntry(); -} - -DECLARE_JOB(default_ma_task_start_decimator, (TMicArray&, chanend_t)); -void default_ma_task_start_decimator(TMicArray& mics, chanend_t c_audio_frames){ - mics.ThreadEntry(); +void set_output_channel(chanend_t c_frames_out) +{ + if (s_use_3_stg_decimator) { + assert(s_mics_3stg != nullptr); + s_mics_3stg->OutputHandler.FrameTx.SetChannel(c_frames_out); + } else { + assert(s_mics != nullptr); + s_mics->OutputHandler.FrameTx.SetChannel(c_frames_out); + } } -DECLARE_JOB(default_ma_task_start_pdm_3stg, (TMicArray_3stg_decimator&)); -void default_ma_task_start_pdm_3stg(TMicArray_3stg_decimator& mics){ - mics.PdmRx.ThreadEntry(); -} +void shutdown_mic_array(void) +{ + if (s_use_3_stg_decimator) { + s_mics_3stg->~TMicArray_3stg_decimator(); + } + else { + s_mics->~TMicArray(); + } -DECLARE_JOB(default_ma_task_start_decimator_3stg, (TMicArray_3stg_decimator&, chanend_t)); -void default_ma_task_start_decimator_3stg(TMicArray_3stg_decimator& mics, chanend_t c_audio_frames){ - mics.ThreadEntry(); + s_mics_3stg = nullptr; + s_mics = nullptr; } #if defined(__XS3A__) #define CLEAR_KEDI() asm volatile("clrsr %0" : : "n"(XS1_SR_KEDI_MASK)); +#elif defined(__VX4B__) +// VX4 processors do not have a dual-issue mode due to VLIW instructions. +// Remove any definition of CLEAR_KEDI so any acciddental use of it will be caught at compile time. +#undef CLEAR_KEDI #else -#define CLEAR_KEDI() ((void)0) // not defined in !xs3a +#undef CLEAR_KEDI // Catch at compile time if attempting to use CLEAR_KEDI on unsupported architectures. #endif template void start_mics_with_pdm_isr(TMics* mics_ptr, chanend_t c_frames_out) { assert(mics_ptr != nullptr); - CLEAR_KEDI(); + + #if defined(__XS3A__) + CLEAR_KEDI(); // Disable dual-issue mode on XS3A processors. VX4 processors do not have a dual-issue mode. + #endif + mics_ptr->OutputHandler.FrameTx.SetChannel(c_frames_out); mics_ptr->PdmRx.AssertOnDroppedBlock(false); mics_ptr->PdmRx.InstallISR(); @@ -122,56 +164,54 @@ void start_mics_with_pdm_isr(TMics* mics_ptr, chanend_t c_frames_out) mics_ptr->ThreadEntry(); } -void mic_array_start( - chanend_t c_frames_out) +void start_mic_array_pdm_isr(chanend_t c_frames_out) { #if MIC_ARRAY_CONFIG_USE_PDM_ISR - if (use_3_stg_decimator) { - start_mics_with_pdm_isr(g_mics_3stg, c_frames_out); + if (s_use_3_stg_decimator) { + start_mics_with_pdm_isr(s_mics_3stg, c_frames_out); } else { - start_mics_with_pdm_isr(g_mics, c_frames_out); - } -#else - if (use_3_stg_decimator) { - assert(g_mics_3stg != nullptr); // Attempting to start mic_array before initialising it - g_mics_3stg->OutputHandler.FrameTx.SetChannel(c_frames_out); - PAR_JOBS( - PJOB(default_ma_task_start_pdm_3stg, (*g_mics_3stg)), - PJOB(default_ma_task_start_decimator_3stg, (*g_mics_3stg, c_frames_out))); - } - else - { - g_mics->OutputHandler.FrameTx.SetChannel(c_frames_out); - PAR_JOBS( - PJOB(default_ma_task_start_pdm, (*g_mics)), - PJOB(default_ma_task_start_decimator, (*g_mics, c_frames_out))); + start_mics_with_pdm_isr(s_mics, c_frames_out); } #endif - // shutdown - if (use_3_stg_decimator) { - g_mics_3stg->~TMicArray_3stg_decimator(); - g_mics_3stg = nullptr; - } - else { - g_mics->~TMicArray(); - g_mics = nullptr; - } } + +// Helper functions for starting separate tasks +void start_pdm_task(void) +{ + s_mics->PdmRx.ThreadEntry(); +} + +void start_decimator_task(void) +{ + s_mics->ThreadEntry(); +} + +void start_pdm_task_3stg(void) +{ + s_mics_3stg->PdmRx.ThreadEntry(); +} + +void start_decimator_task_3stg(void) +{ + s_mics_3stg->ThreadEntry(); +} + // Override pdm data port. Only used in tests where a chanend is used as a 'port' for input pdm data. void _mic_array_override_pdm_port(chanend_t c_pdm) { - if (use_3_stg_decimator) { - assert(g_mics_3stg != nullptr); - g_mics_3stg->PdmRx.SetPort((port_t)c_pdm); + if (s_use_3_stg_decimator) { + assert(s_mics_3stg != nullptr); + s_mics_3stg->PdmRx.SetPort((port_t)c_pdm); } else { - assert(g_mics != nullptr); - g_mics->PdmRx.SetPort((port_t)c_pdm); + assert(s_mics != nullptr); + s_mics->PdmRx.SetPort((port_t)c_pdm); } } // C wrapper -extern "C" void _mic_array_override_pdm_port_c(chanend_t c_pdm) +MA_C_API +void _mic_array_override_pdm_port_c(chanend_t c_pdm) { _mic_array_override_pdm_port(c_pdm); } diff --git a/lib_mic_array/src/mic_array_task_internal.hpp b/lib_mic_array/src/mic_array_task_internal.hpp index 33386bd8..97536b25 100644 --- a/lib_mic_array/src/mic_array_task_internal.hpp +++ b/lib_mic_array/src/mic_array_task_internal.hpp @@ -6,6 +6,7 @@ #include "mic_array.h" #include "mic_array/etc/filters_default.h" +#ifdef __cplusplus using TMicArray = mic_array::MicArray, mic_array::StandardPdmRxService>; -union UAnyMicArray { - TMicArray m_2stg; - TMicArray_3stg_decimator m_3stg; -}; - union UStg2_filter_state { int32_t filter_state_df_6[MIC_ARRAY_CONFIG_MIC_COUNT][STAGE2_TAP_COUNT]; int32_t filter_state_df_3[MIC_ARRAY_CONFIG_MIC_COUNT][MIC_ARRAY_32K_STAGE_2_TAP_COUNT]; @@ -54,11 +50,9 @@ union UPdmRx_out_block_double_buf { uint32_t __attribute__((aligned (8))) out_block_double_buf_df_2[2][MIC_ARRAY_CONFIG_MIC_IN_COUNT * 2]; }; -extern TMicArray* g_mics; - -UStg2_filter_state stg2_filter_state_mem; -UPdmRx_out_block pdm_rx_out_block; -UPdmRx_out_block_double_buf __attribute__((aligned (8))) pdm_rx_out_block_double_buf; // deinterleave() functions expect dword alignment +union UStg2_filter_state stg2_filter_state_mem; +union UPdmRx_out_block pdm_rx_out_block; +union UPdmRx_out_block_double_buf __attribute__((aligned (8))) pdm_rx_out_block_double_buf; // deinterleave() functions expect dword alignment inline const uint32_t* stage_1_filter(unsigned stg2_dec_factor) { // stg2 decimation factor also seems to affect the stage1 filter used @@ -91,45 +85,37 @@ inline uint32_t* get_pdm_rx_out_block_double_buf(unsigned stg2_dec_factor) { : (uint32_t*)pdm_rx_out_block_double_buf.out_block_double_buf_df_2); } -inline void init_mics_default_filter(TMicArray* m, pdm_rx_resources_t* pdm_res, const unsigned* channel_map, unsigned stg2_dec_factor) { - static int32_t stg1_filter_state[MIC_ARRAY_CONFIG_MIC_COUNT][8]; - mic_array_decimator_conf_t decimator_conf; - memset(&decimator_conf, 0, sizeof(decimator_conf)); - mic_array_filter_conf_t filter_conf[2] = {{0}}; - - // decimator - decimator_conf.filter_conf = &filter_conf[0]; - decimator_conf.num_filter_stages = 2; - //filter stage 1 - filter_conf[0].coef = (int32_t*)stage_1_filter(stg2_dec_factor); - filter_conf[0].num_taps = 256; - filter_conf[0].decimation_factor = 32; - filter_conf[0].shr = 0; - filter_conf[0].state_words_per_channel = filter_conf[0].num_taps/32; - filter_conf[0].state = (int32_t*)stg1_filter_state; - - // filter stage 2 - filter_conf[1].coef = (int32_t*)stage_2_filter(stg2_dec_factor); - filter_conf[1].num_taps = stage_2_num_taps(stg2_dec_factor); - filter_conf[1].decimation_factor = stg2_dec_factor; - filter_conf[1].shr = stage_2_shift(stg2_dec_factor); - filter_conf[1].state_words_per_channel = decimator_conf.filter_conf[1].num_taps; - filter_conf[1].state = stage_2_state_memory(stg2_dec_factor); - - m->Decimator.Init(decimator_conf); - - pdm_rx_conf_t pdm_rx_config; - pdm_rx_config.pdm_out_words_per_channel = stg2_dec_factor; - pdm_rx_config.pdm_out_block = get_pdm_rx_out_block(stg2_dec_factor); - pdm_rx_config.pdm_in_double_buf = get_pdm_rx_out_block_double_buf(stg2_dec_factor); - - - m->PdmRx.Init(pdm_res->p_pdm_mics, pdm_rx_config); - - if(channel_map) { - m->PdmRx.MapChannels(channel_map); - } - int divide = pdm_res->mclk_freq / pdm_res->pdm_freq; - mic_array_resources_configure(pdm_res, divide); - mic_array_pdm_clock_start(pdm_res); -} +#endif // __cplusplus + +MA_C_API +bool get_decimator_stg_count(void); + +MA_C_API +void init_mic_array_storage(bool use_3_stg_decimator); + +MA_C_API +void init_mics_custom_filter(pdm_rx_resources_t* pdm_res, mic_array_conf_t* mic_array_conf); + +MA_C_API +void init_mics_default_filter(pdm_rx_resources_t* pdm_res, const unsigned* channel_map, unsigned stg2_dec_factor); + +MA_C_API +void set_output_channel(chanend_t c_frames_out); + +MA_C_API +void shutdown_mic_array(void); + +MA_C_API +void start_decimator_task(void); + +MA_C_API +void start_decimator_task_3stg(void); + +MA_C_API +void start_mic_array_pdm_isr(chanend_t c_frames_out); + +MA_C_API +void start_pdm_task(void); + +MA_C_API +void start_pdm_task_3stg(void); diff --git a/lib_mic_array/src/pdm_rx_isr.S b/lib_mic_array/src/pdm_rx_isr.S index 6c649f1e..8c0cfaa5 100644 --- a/lib_mic_array/src/pdm_rx_isr.S +++ b/lib_mic_array/src/pdm_rx_isr.S @@ -129,4 +129,18 @@ FUNCTION_NAME: .resource_list_empty FUNCTION_NAME, "tail_callees" .resource_list_empty FUNCTION_NAME, "parallel_callees" +.section .data.pdm_rx_isr_context, "aw" +.p2align 1 +.globl pdm_rx_isr_context + +pdm_rx_isr_context: + .word 0 // .L_port + .word 0 // .L_buffA + .word 0 // .L_buffB + .word 0 // .L_phase1 + .word 0 // .L_phase1_reset + .word 0 // .L_c_out + .word 0 // .L_credit + .word -1 // .L_missed_blocks + #endif // __VX4A__ || __VX4B__ diff --git a/tests/signal/profile/mic_array_mips.json b/tests/signal/profile/mic_array_mips.json index 12f472d2..b79b29e2 100644 --- a/tests/signal/profile/mic_array_mips.json +++ b/tests/signal/profile/mic_array_mips.json @@ -1,14 +1,14 @@ { - "1mic_isr_16000fs": 14.1459, + "1mic_isr_16000fs": 14.1458, "1mic_isr_32000fs": 17.2336, - "1mic_isr_48000fs": 21.3055, - "1mic_thread_16000fs": 12.9298, - "1mic_thread_32000fs": 15.9536, - "1mic_thread_48000fs": 19.9614, - "2mic_isr_16000fs": 29.3098, - "2mic_isr_32000fs": 34.6215, - "2mic_isr_48000fs": 41.9335, - "2mic_thread_16000fs": 27.0056, - "2mic_thread_32000fs": 32.2854, - "2mic_thread_48000fs": 39.5335 + "1mic_isr_48000fs": 21.3054, + "1mic_thread_16000fs": 12.8978, + "1mic_thread_32000fs": 15.9857, + "1mic_thread_48000fs": 20.0573, + "2mic_isr_16000fs": 29.3336, + "2mic_isr_32000fs": 34.6533, + "2mic_isr_48000fs": 41.9592, + "2mic_thread_16000fs": 26.8137, + "2mic_thread_32000fs": 32.0934, + "2mic_thread_48000fs": 39.3415 } \ No newline at end of file diff --git a/tests/signal/profile/mic_array_mips_table.rst b/tests/signal/profile/mic_array_mips_table.rst index b5f738ef..b4272514 100644 --- a/tests/signal/profile/mic_array_mips_table.rst +++ b/tests/signal/profile/mic_array_mips_table.rst @@ -23,36 +23,36 @@ * - 1 - THREAD - 16000 - - 12.930 + - 12.898 * - 1 - THREAD - 32000 - - 15.954 + - 15.986 * - 1 - THREAD - 48000 - - 19.961 + - 20.057 * - 2 - ISR - 16000 - - 29.310 + - 29.334 * - 2 - ISR - 32000 - - 34.621 + - 34.653 * - 2 - ISR - 48000 - - 41.934 + - 41.959 * - 2 - THREAD - 16000 - - 27.006 + - 26.814 * - 2 - THREAD - 32000 - - 32.285 + - 32.093 * - 2 - THREAD - 48000 - - 39.533 \ No newline at end of file + - 39.342 \ No newline at end of file