From b6b37b1c7347ce638549acfbe4b80c9915609dc7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?xalbertoisorna=E2=80=9D?= Date: Fri, 20 Feb 2026 16:00:32 +0000 Subject: [PATCH 1/2] Replacing BasicMicArray Test XC with C code --- lib_mic_array/src/mic_array_task.cpp | 11 +- tests/signal/BasicMicArray/CMakeLists.txt | 3 +- .../BasicMicArray/src/{app.cpp => app.c} | 235 +++++++++++++----- tests/signal/BasicMicArray/src/app.h | 26 -- tests/signal/BasicMicArray/src/main.xc | 162 ------------ 5 files changed, 188 insertions(+), 249 deletions(-) rename tests/signal/BasicMicArray/src/{app.cpp => app.c} (69%) delete mode 100644 tests/signal/BasicMicArray/src/app.h delete mode 100644 tests/signal/BasicMicArray/src/main.xc diff --git a/lib_mic_array/src/mic_array_task.cpp b/lib_mic_array/src/mic_array_task.cpp index 962ac90a..a2815785 100644 --- a/lib_mic_array/src/mic_array_task.cpp +++ b/lib_mic_array/src/mic_array_task.cpp @@ -19,7 +19,7 @@ bool use_3_stg_decimator = false; // until mic_array_start() completes. mic_array_start() performs shutdown and // then sets g_mics back to nullptr. -#ifdef __XS3A__ +#if defined(__XS3A__) || defined(__VX4B__) //////////////////// // Mic array init // //////////////////// @@ -171,4 +171,11 @@ void _mic_array_override_pdm_port(chanend_t c_pdm) g_mics->PdmRx.SetPort((port_t)c_pdm); } } -#endif + +// C wrapper +extern "C" void _mic_array_override_pdm_port_c(chanend_t c_pdm) +{ + _mic_array_override_pdm_port(c_pdm); +} + +#endif // __XS3A__ or __VX4B__ diff --git a/tests/signal/BasicMicArray/CMakeLists.txt b/tests/signal/BasicMicArray/CMakeLists.txt index 97a1e98f..86674faf 100644 --- a/tests/signal/BasicMicArray/CMakeLists.txt +++ b/tests/signal/BasicMicArray/CMakeLists.txt @@ -4,7 +4,7 @@ project(test_ma) set(XMOS_SANDBOX_DIR ${CMAKE_CURRENT_LIST_DIR}/../../../..) -include(${CMAKE_CURRENT_LIST_DIR}/../../../examples/deps.cmake) +set(APP_DEPENDENT_MODULES "lib_mic_array") set(APP_HW_TARGET XK-EVK-XU316) @@ -113,4 +113,3 @@ foreach(target ${APP_BUILD_TARGETS}) add_dependencies(${target} gen_custom_filter) endif() endforeach() - diff --git a/tests/signal/BasicMicArray/src/app.cpp b/tests/signal/BasicMicArray/src/app.c similarity index 69% rename from tests/signal/BasicMicArray/src/app.cpp rename to tests/signal/BasicMicArray/src/app.c index d8b8aeda..b8f991f0 100644 --- a/tests/signal/BasicMicArray/src/app.cpp +++ b/tests/signal/BasicMicArray/src/app.c @@ -1,30 +1,36 @@ // Copyright 2022-2026 XMOS LIMITED. // This Software is subject to the terms of the XMOS Public Licence: Version 1. -#include -#include -#include -#include +#include +#include +#include #include -#include #include - +#include +#include +#include #include #include #include - -extern "C" { -#include "xscope.h" -} +#include #include "mic_array.h" -#include "app.h" #if USE_CUSTOM_FILTER #include "custom_filter.h" #endif +#define BUFF_SIZE (256) + +typedef chanend_t streaming_chanend_t; + +DECLARE_JOB(app_output_task, (chanend_t, chanend_t)); +DECLARE_JOB(app_fifo_to_xscope_task, (chanend_t)); +DECLARE_JOB(app_mic, (chanend_t, chanend_t)); +DECLARE_JOB(host_words_to_app, (chanend_t, streaming_chanend_t)); + + typedef struct { unsigned stg1_tap_count; unsigned stg1_decimation_factor; @@ -39,14 +45,24 @@ typedef struct { int32_t *stg3_coef_ptr; }filt_config_t; -static void get_filter_config(unsigned fs, filt_config_t *cfg) { +// ------------------------------- HELPER FUNCTIONS ------------------------------- + +static inline +void hwtimer_delay_microseconds(unsigned delay) { + hwtimer_t tmr = hwtimer_alloc(); + hwtimer_delay(tmr, delay * XS1_TIMER_MHZ); + hwtimer_free(tmr); +} + +static +void get_filter_config(unsigned fs, filt_config_t *cfg) { #if !USE_CUSTOM_FILTER cfg->stg1_tap_count = 256; cfg->stg1_decimation_factor = 32; cfg->stg3_tap_count = 0; cfg->stg3_decimation_factor = 1; // set as 1 to not mess up blocksize calculation at the host end for 2 stage decimator - cfg->stg3_coef_ptr = nullptr; + cfg->stg3_coef_ptr = NULL; cfg->stg3_shr = 0; if(fs == 16000) { cfg->stg2_tap_count = STAGE2_TAP_COUNT; @@ -91,7 +107,102 @@ static void get_filter_config(unsigned fs, filt_config_t *cfg) { #endif } -extern void _mic_array_override_pdm_port(chanend_t c_pdm); +static inline +void app_print_filters() +{ + filt_config_t filt_cfg; + get_filter_config(APP_SAMP_FREQ, &filt_cfg); + + unsigned stg1_tap_words = filt_cfg.stg1_tap_count / 2; + + + printf("stage1 filter length: %d 16bit coefs -> %d 32b words\n", stg1_tap_words*2, stg1_tap_words); + int initial_list = stg1_tap_words/4; + printf("stage1_coef = [\n"); + for(int a = 0; a < initial_list; a++){ + printf("0x%08X, 0x%08X, 0x%08X, 0x%08X, \n", + filt_cfg.stg1_coef_ptr[a*4+0], filt_cfg.stg1_coef_ptr[a*4+1], + filt_cfg.stg1_coef_ptr[a*4+2], filt_cfg.stg1_coef_ptr[a*4+3]); + } + for(int a = initial_list*4; a < stg1_tap_words; a++){ + printf("0x%08X, ", filt_cfg.stg1_coef_ptr[a]); + } + printf("]\n"); + + printf("stage2 filter length: %d\n", filt_cfg.stg2_tap_count); + printf("stage2_coef = [\n"); + initial_list = filt_cfg.stg2_tap_count/4; + for(int a = 0; a < initial_list; a++){ + printf("0x%08X, 0x%08X, 0x%08X, 0x%08X, \n", + filt_cfg.stg2_coef_ptr[4*a+0], filt_cfg.stg2_coef_ptr[4*a+1], + filt_cfg.stg2_coef_ptr[4*a+2], filt_cfg.stg2_coef_ptr[4*a+3]); + } + for(int a = initial_list*4; a < filt_cfg.stg2_tap_count; a++){ + printf("0x%08X, ", filt_cfg.stg2_coef_ptr[a]); + } + printf("]\n"); + + printf("stage2_shr = %d\n", filt_cfg.stg2_shr); +} + +static inline +void cmd_print_msg(unsigned i){ + const char* msg[4] = { + "[CMD] Resume data loop.\n", + "[CMD] Terminate application.\n", + "[CMD] Print Filters.\n", + "[CMD] Unknown command.\n" + }; + printf("%s\n", msg[i]); +} + +static inline +void cmd_perform_action(unsigned cmd){ + switch(cmd){ + case 0: break; + case 1: exit(0); break; + case 2: app_print_filters(); break; + default: assert(0); break; + } +} + +static inline +void cmd_loop(chanend_t c_from_host) +{ + char cmd_buff[BUFF_SIZE]; + int pp; + SELECT_RES( + CASE_THEN(c_from_host, c_from_host_handler) + ) + c_from_host_handler:{ + xscope_data_from_host(c_from_host, &cmd_buff[0], &pp); + assert((pp-1) == 4); + uint32_t cmd = ((uint32_t*)(void*) &cmd_buff[0])[0]; + cmd_print_msg(cmd); + cmd_perform_action(cmd); + continue; + } +} + +static inline +int send_words_to_app(streaming_chanend_t c_to_app, char* buff, int buff_lvl) +{ + int* next_word = (int*)(void*) &buff[0]; + while(buff_lvl >= sizeof(int)){ + s_chan_out_word(c_to_app, next_word[0]); + next_word++; + buff_lvl -= sizeof(int); + hwtimer_delay_microseconds(15); + } + if(buff_lvl) + { + memmove(&buff[0], &next_word[0], buff_lvl); + } + return buff_lvl; +} + + +extern void _mic_array_override_pdm_port_c(chanend_t c_pdm); // We'll be using a fairly standard mic array setup here, with one big // exception. Instead of feeding the PDM rx service with a port, we're going @@ -110,15 +221,15 @@ pdm_rx_resources_t pdm_res = PDM_RX_RESOURCES_SDR( XS1_CLKBLK_1); #if USE_CUSTOM_FILTER -void init_mic_conf(mic_array_conf_t &mic_array_conf, mic_array_filter_conf_t (&filter_conf)[NUM_DECIMATION_STAGES], unsigned *channel_map) +static void init_mic_conf(mic_array_conf_t *mic_array_conf, mic_array_filter_conf_t filter_conf[NUM_DECIMATION_STAGES], unsigned *channel_map) { static int32_t stg1_filter_state[MIC_ARRAY_CONFIG_MIC_COUNT][8]; static int32_t stg2_filter_state[MIC_ARRAY_CONFIG_MIC_COUNT][CUSTOM_FILTER_STG2_TAP_COUNT]; - memset(&mic_array_conf, 0, sizeof(mic_array_conf_t)); + memset(mic_array_conf, 0, sizeof(mic_array_conf_t)); //decimator - mic_array_conf.decimator_conf.filter_conf = &filter_conf[0]; - mic_array_conf.decimator_conf.num_filter_stages = NUM_DECIMATION_STAGES; + mic_array_conf->decimator_conf.filter_conf = &filter_conf[0]; + mic_array_conf->decimator_conf.num_filter_stages = NUM_DECIMATION_STAGES; // stage 1 filter_conf[0].coef = (int32_t*)custom_filter_stg1_coef; filter_conf[0].num_taps = CUSTOM_FILTER_STG1_TAP_COUNT; @@ -148,13 +259,16 @@ void init_mic_conf(mic_array_conf_t &mic_array_conf, mic_array_filter_conf_t (&f // pdm rx static uint32_t pdmrx_out_block[MIC_ARRAY_CONFIG_MIC_COUNT][CUSTOM_FILTER_STG2_DECIMATION_FACTOR * CUSTOM_FILTER_STG3_DECIMATION_FACTOR]; static uint32_t __attribute__((aligned(8))) pdmrx_out_block_double_buf[2][MIC_ARRAY_CONFIG_MIC_COUNT * CUSTOM_FILTER_STG2_DECIMATION_FACTOR * CUSTOM_FILTER_STG3_DECIMATION_FACTOR]; - mic_array_conf.pdmrx_conf.pdm_out_words_per_channel = CUSTOM_FILTER_STG2_DECIMATION_FACTOR * CUSTOM_FILTER_STG3_DECIMATION_FACTOR; - mic_array_conf.pdmrx_conf.pdm_out_block = (uint32_t*)pdmrx_out_block; - mic_array_conf.pdmrx_conf.pdm_in_double_buf = (uint32_t*)pdmrx_out_block_double_buf; - mic_array_conf.pdmrx_conf.channel_map = channel_map; + mic_array_conf->pdmrx_conf.pdm_out_words_per_channel = CUSTOM_FILTER_STG2_DECIMATION_FACTOR * CUSTOM_FILTER_STG3_DECIMATION_FACTOR; + mic_array_conf->pdmrx_conf.pdm_out_block = (uint32_t*)pdmrx_out_block; + mic_array_conf->pdmrx_conf.pdm_in_double_buf = (uint32_t*)pdmrx_out_block_double_buf; + mic_array_conf->pdmrx_conf.channel_map = channel_map; } #endif + +// ------------------------------- THREADS ------------------------------- + void app_mic( chanend_t c_pdm_in, chanend_t c_frames_out) //non-streaming @@ -164,10 +278,10 @@ void app_mic( #else mic_array_conf_t mic_array_conf; mic_array_filter_conf_t filter_conf[NUM_DECIMATION_STAGES]; - init_mic_conf(mic_array_conf, filter_conf, NULL); + init_mic_conf(&mic_array_conf, filter_conf, NULL); mic_array_init_custom_filter(&pdm_res, &mic_array_conf); #endif - _mic_array_override_pdm_port((port_t)c_pdm_in); // get pdm input from channel instead of port. + _mic_array_override_pdm_port_c((port_t)c_pdm_in); // get pdm input from channel instead of port. // mic_array_init() calls mic_array_resources_configure which would crash // if a chanend were to be passed instead of a port for the pdm data port, so // this overriding has to be done only after calling mic_array_init() @@ -219,6 +333,7 @@ void app_output_task(chanend_t c_frames_in, chanend_t c_fifo) } } + void app_fifo_to_xscope_task(chanend_t c_fifo) { while(1){ @@ -235,40 +350,46 @@ void app_fifo_to_xscope_task(chanend_t c_fifo) } - -void app_print_filters() +void host_words_to_app(chanend_t c_from_host, streaming_chanend_t c_to_app) { - filt_config_t filt_cfg; - get_filter_config(APP_SAMP_FREQ, &filt_cfg); - - unsigned stg1_tap_words = filt_cfg.stg1_tap_count / 2; - - - printf("stage1 filter length: %d 16bit coefs -> %d 32b words\n", stg1_tap_words*2, stg1_tap_words); - int initial_list = stg1_tap_words/4; - printf("stage1_coef = [\n"); - for(int a = 0; a < initial_list; a++){ - printf("0x%08X, 0x%08X, 0x%08X, 0x%08X, \n", - filt_cfg.stg1_coef_ptr[a*4+0], filt_cfg.stg1_coef_ptr[a*4+1], - filt_cfg.stg1_coef_ptr[a*4+2], filt_cfg.stg1_coef_ptr[a*4+3]); - } - for(int a = initial_list*4; a < stg1_tap_words; a++){ - printf("0x%08X, ", filt_cfg.stg1_coef_ptr[a]); - } - printf("]\n"); + xscope_connect_data_from_host(c_from_host); + + char buff[100*BUFF_SIZE+3]; + int buff_lvl = 0; + int dd = 0; + + SELECT_RES( + CASE_THEN(c_from_host, c_from_host_handler) + ){ + c_from_host_handler:{ + xscope_data_from_host(c_from_host, &buff[0], &dd); + dd--; + buff_lvl += dd; + if(dd == 0) { + cmd_loop(c_from_host); + } + else { + buff_lvl = send_words_to_app(c_to_app, buff, buff_lvl); + } + continue; + }} +} - printf("stage2 filter length: %d\n", filt_cfg.stg2_tap_count); - printf("stage2_coef = [\n"); - initial_list = filt_cfg.stg2_tap_count/4; - for(int a = 0; a < initial_list; a++){ - printf("0x%08X, 0x%08X, 0x%08X, 0x%08X, \n", - filt_cfg.stg2_coef_ptr[4*a+0], filt_cfg.stg2_coef_ptr[4*a+1], - filt_cfg.stg2_coef_ptr[4*a+2], filt_cfg.stg2_coef_ptr[4*a+3]); - } - for(int a = initial_list*4; a < filt_cfg.stg2_tap_count; a++){ - printf("0x%08X, ", filt_cfg.stg2_coef_ptr[a]); - } - printf("]\n"); - printf("stage2_shr = %d\n", filt_cfg.stg2_shr); +int main(){ + channel_t c_frames = chan_alloc(); + channel_t c_fifo = chan_alloc(); + chanend_t xscope_chan = chanend_alloc(); // tools will start the other channel end + streaming_channel_t c_to_app = s_chan_alloc(); + + PAR_JOBS( + PJOB(app_mic, (c_to_app.end_a, c_frames.end_a)), + PJOB(app_output_task, (c_frames.end_b, c_fifo.end_a)), + PJOB(app_fifo_to_xscope_task, (c_fifo.end_b)), + PJOB(host_words_to_app, (xscope_chan, c_to_app.end_b)) + ); + s_chan_free(c_to_app); + chan_free(c_frames); + chan_free(c_fifo); + return 0; } diff --git a/tests/signal/BasicMicArray/src/app.h b/tests/signal/BasicMicArray/src/app.h deleted file mode 100644 index c1c45572..00000000 --- a/tests/signal/BasicMicArray/src/app.h +++ /dev/null @@ -1,26 +0,0 @@ -// Copyright 2022-2026 XMOS LIMITED. -// This Software is subject to the terms of the XMOS Public Licence: Version 1. - -#pragma once - -#include "mic_array.h" - -C_API_START - -// Thread that runs the decimator -MA_C_API -void app_mic(chanend_t c_pdm_in, chanend_t c_frames_out); - -// Thread that receives samples and pushes them into a FIFO -MA_C_API -void app_output_task(chanend_t c_frames_in, chanend_t c_fifo); - -// Thread that sends FIFO results back to host -MA_C_API -void app_fifo_to_xscope_task(chanend_t c_fifo); - -// Print filters (for debug purposes) -MA_C_API -void app_print_filters(); - -C_API_END diff --git a/tests/signal/BasicMicArray/src/main.xc b/tests/signal/BasicMicArray/src/main.xc deleted file mode 100644 index 0141041d..00000000 --- a/tests/signal/BasicMicArray/src/main.xc +++ /dev/null @@ -1,162 +0,0 @@ -// Copyright 2022-2026 XMOS LIMITED. -// This Software is subject to the terms of the XMOS Public Licence: Version 1. - -#include -#include -#include - -#include -#include - -#include "app.h" - -unsafe { - - -// We cannot be guaranteed to read less than this, and we cannot read more than -// this -#define BUFF_SIZE 256 - -void host_words_to_app( - chanend c_from_host, - streaming chanend c_to_app) -{ - xscope_connect_data_from_host(c_from_host); - - // +3 is for any partial word at the end of the read. It will get moved to the - // front and we'll read again, which could be another BUFF_SIZE bytes - char buff[100*BUFF_SIZE+3]; - int buff_lvl = 0; - - /* - The host app can attempt to send [0, 255] bytes on a write attempt. The - xscope logic always adds a null byte at the end, so we expect to receive - [1,256] bytes here. We ignore the null byte at the end, and just consider - the other bytes (i.e. we pretend we received one fewer byte). Then here's - the basic protocol: - - - Loop forever: - - Receive N bytes of data. (after subtracting 1) - - if N > 0: send all whole words to app and loop back. - - if N == 0: - - Enter inner (command) loop: - - Listen for 1 word (4 bytes) from host: - - assert() 4 bytes were received - - Interpret command based on received word value: - - CMD 0: break from command loop back to data loop - - CMD 1: terminate application - - The reason for the command loop is so that the application can terminate - gracefully. This design should also make it relatively easy to add - additional commands if need be. - */ - while(1){ - int dd; - select { - case xscope_data_from_host(c_from_host, &buff[0], dd): - { - dd--; // last byte is always 0 (for some reason) - buff_lvl += dd; - - if(dd == 0){ - // Enter command loop - - // use a separate buffer in case there is a fractional word in the - // data buffer. - char cmd_buff[BUFF_SIZE]; - int cmd_loop = 1; - while(cmd_loop){ - int pp; - select { - case xscope_data_from_host(c_from_host, &cmd_buff[0], pp): - { - assert( (pp-1) == 4 ); - uint32_t cmd = ((uint32_t*)(void*) &cmd_buff[0])[0]; - if(cmd == 0){ - // Break back to data loop - printf("[CMD] Resume data loop.\n"); - cmd_loop = 0; - } else if (cmd == 1){ - // Terminate application - printf("[CMD] Terminate application.\n"); - exit(0); - } else if (cmd == 2){ - // Print filters - printf("[CMD] Print Filters.\n"); - app_print_filters(); - cmd_loop = 0; - } else { - // Unknown command. - printf("[CMD] Unknown command.\n"); - assert(0); - } - break; - } - } - } - - } else { - - // Send all (complete) words to app - int* next_word = ((int*) (void*) &buff[0]); - while(buff_lvl >= sizeof(int)){ - c_to_app <: next_word[0]; - next_word++; - buff_lvl -= sizeof(int); - // We have to be careful about how quickly we push data to the PDM - // rx service. Sending too quickly can mess stuff up or (if PDM rx - // is running in ISR mode) cause a deadlock. This is unfortunate, - // but is only because we're pretending a channel is a port. - // Ultimately, we just need to avoid sending PDM data faster than - // the decimator thread can process the data. Unfortunately, because - // frames are being used, we don't actually know when it's done - // processing a given block of data. So, we'll just insert a - // reasonable delay here. - - // With a 3.072 MHz PDM clock and 1 microphone, we expect to have - // port reads at a rate of 3.072 MHz / 32 = 96 kHz. That rate is - // linear in the number of channels, but we're also always safe if - // we go slower than we need to here. (96 kHz)^(-1) = 10.41 us - delay_microseconds(15); - } - - // if there's 1-3 bytes left move it to the front. - if(buff_lvl) memmove(&buff[0], &next_word[0], buff_lvl); - } - break; - } - } - - // repeat forever - } -} - - -int main() -{ - chan c_from_host; - streaming chan c_to_app; - chan c_frames; - chan c_fifo; - - par { - xscope_host_data(c_from_host); - - on tile[0]: { - host_words_to_app(c_from_host, c_to_app); - } - - on tile[0]: { - xscope_mode_lossless(); - - par { - app_mic((chanend_t) c_to_app, (chanend_t) c_frames); - app_output_task((chanend_t) c_frames, (chanend_t)c_fifo); - app_fifo_to_xscope_task((chanend_t)c_fifo); - } - } - } - return 0; -} - -} From cfe7456cfe78d1b2f80addf2d3d3baa9a402c16f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?xalbertoisorna=E2=80=9D?= Date: Fri, 20 Feb 2026 16:21:07 +0000 Subject: [PATCH 2/2] Update conditional compilation for mic array initialization to exclude XS2A --- lib_mic_array/src/mic_array_task.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib_mic_array/src/mic_array_task.cpp b/lib_mic_array/src/mic_array_task.cpp index a2815785..3a0f2812 100644 --- a/lib_mic_array/src/mic_array_task.cpp +++ b/lib_mic_array/src/mic_array_task.cpp @@ -19,7 +19,7 @@ bool use_3_stg_decimator = false; // until mic_array_start() completes. mic_array_start() performs shutdown and // then sets g_mics back to nullptr. -#if defined(__XS3A__) || defined(__VX4B__) +#if !defined(XS2A) //////////////////// // Mic array init // //////////////////// @@ -178,4 +178,4 @@ extern "C" void _mic_array_override_pdm_port_c(chanend_t c_pdm) _mic_array_override_pdm_port(c_pdm); } -#endif // __XS3A__ or __VX4B__ +#endif // !defined(XS2A)