diff --git a/.gitignore b/.gitignore
index 833049a0..e50d6bee 100644
--- a/.gitignore
+++ b/.gitignore
@@ -51,3 +51,5 @@ build.xcore
# Python cache information
lib_mic_array.egg-info
+examples/app_mic_array_basic/output.wav
+examples/app_mic_array_basic/mic_array_output.bin
diff --git a/Jenkinsfile b/Jenkinsfile
index 13b738c2..b33fc207 100644
--- a/Jenkinsfile
+++ b/Jenkinsfile
@@ -15,8 +15,13 @@ pipeline {
string(
name: 'XMOSDOC_VERSION',
defaultValue: 'v8.0.1',
- description: 'The xmosdoc version')
-
+ description: 'The xmosdoc version'
+ )
+ string(
+ name: 'TOOLS_VX4_VERSION',
+ defaultValue: '-j --repo arch_vx_slipgate -b master -a XTC 112',
+ description: 'The XTC Slipgate tools version'
+ )
string(
name: 'INFR_APPS_VERSION',
defaultValue: 'v3.3.0',
@@ -139,10 +144,8 @@ pipeline {
stage('Tests') {
parallel {
- stage('XS3 tests') {
- agent {
- label 'xcore.ai'
- }
+ stage('XS3 Tests') {
+ agent {label 'xcore.ai'}
stages {
stage("Checkout and Build") {
steps {
@@ -209,11 +212,40 @@ pipeline {
} // stage('Run tests')
} // stages
post {
- cleanup {
- xcoreCleanSandbox()
- }
- }
- } // stage('HW tests')
+ cleanup {xcoreCleanSandbox()}
+ } // post
+ } // XS3 Tests
+
+ stage('VX4 Tests') {
+ agent {label "vx4"}
+ stages {
+ stage("Checkout and Build") {
+ steps {
+ dir(REPO_NAME){
+ checkoutScmShallow()
+ dir("tests/unit") {
+ xcoreBuild(buildTool: "xmake", toolsVersion: params.TOOLS_VX4_VERSION)
+ }
+ dir ("tests/signal/BasicMicArray") {
+ createVenv(reqFile: "requirements.txt")
+ withVenv {
+ xcoreBuild(buildTool: "xmake", toolsVersion: params.TOOLS_VX4_VERSION)
+ }
+ }
+ }}
+ } // stage("Checkout and Build")
+ stage('Run tests') {
+ steps {
+ dir(REPO_NAME){
+ dir("tests/unit") {
+ withTools(params.TOOLS_VX4_VERSION) {sh "xrun --xscope bin/tests-unit.xe"}
+ }}}} // stage('Run tests')
+ } // stages
+ post {
+ cleanup {xcoreCleanSandbox()}
+ } //post
+ } // VX4 Tests
+
} // parallel
} // stage('Tests')
diff --git a/doc/exclude_patterns.inc b/doc/exclude_patterns.inc
index 3a948a03..9ddf9cb8 100644
--- a/doc/exclude_patterns.inc
+++ b/doc/exclude_patterns.inc
@@ -7,3 +7,4 @@ LICENSE.rst
build.xcore
tests/**/.pytest_cache/*.md
tests/.pytest_cache/*.md
+**/app_mic_array_basic/*.md
diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt
index 2e687aa0..99700f7d 100644
--- a/examples/CMakeLists.txt
+++ b/examples/CMakeLists.txt
@@ -2,6 +2,7 @@ cmake_minimum_required(VERSION 3.21)
include($ENV{XMOS_CMAKE_PATH}/xcommon.cmake)
project(mic_array_examples)
add_subdirectory(app_mic_array)
+add_subdirectory(app_mic_array_basic)
add_subdirectory(app_shutdown)
add_subdirectory(app_par_decimator)
add_subdirectory(app_custom_filter)
diff --git a/examples/app_mic_array_basic/CMakeLists.txt b/examples/app_mic_array_basic/CMakeLists.txt
new file mode 100644
index 00000000..6b7da34d
--- /dev/null
+++ b/examples/app_mic_array_basic/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 src/XK-EVK-XU316-AIV.xn)
+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=1
+)
+
+XMOS_REGISTER_APP()
diff --git a/examples/app_mic_array_basic/README.md b/examples/app_mic_array_basic/README.md
new file mode 100644
index 00000000..f548144a
--- /dev/null
+++ b/examples/app_mic_array_basic/README.md
@@ -0,0 +1,30 @@
+# Basic Mic Array Example
+
+## Hardware Required
+
+- **XK-EVK-XU316-AIV.XN**
+
+## 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/convert.py b/examples/app_mic_array_basic/convert.py
new file mode 100644
index 00000000..95ed982a
--- /dev/null
+++ b/examples/app_mic_array_basic/convert.py
@@ -0,0 +1,28 @@
+# 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
+
+APP_OUT_FREQ_HZ = 12000 # 4KHz
+
+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=APP_OUT_FREQ_HZ,
+ bits_per_sample=32
+ )
diff --git a/examples/app_mic_array_basic/mic_array_output.bin b/examples/app_mic_array_basic/mic_array_output.bin
new file mode 100644
index 00000000..4f69a22c
Binary files /dev/null and b/examples/app_mic_array_basic/mic_array_output.bin differ
diff --git a/examples/app_mic_array_basic/output.wav b/examples/app_mic_array_basic/output.wav
new file mode 100644
index 00000000..dc809209
Binary files /dev/null and b/examples/app_mic_array_basic/output.wav differ
diff --git a/examples/app_mic_array_basic/src/XK-EVK-XU316-AIV.xn b/examples/app_mic_array_basic/src/XK-EVK-XU316-AIV.xn
new file mode 100644
index 00000000..b4eb8fff
--- /dev/null
+++ b/examples/app_mic_array_basic/src/XK-EVK-XU316-AIV.xn
@@ -0,0 +1,66 @@
+
+
+ Board
+ xcore.ai Vision Development Kit
+
+
+ tileref tile[2]
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/examples/app_mic_array_basic/src/app.c b/examples/app_mic_array_basic/src/app.c
new file mode 100644
index 00000000..a1d8155c
--- /dev/null
+++ b/examples/app_mic_array_basic/src/app.c
@@ -0,0 +1,117 @@
+// Copyright 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 "mic_array.h"
+#include "device_pll_ctrl.h"
+#include "small_768k_to_12k_filter.h"
+
+#include "app_config.h"
+
+#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 init_mic_conf(mic_array_conf_t *mic_array_conf, mic_array_filter_conf_t filter_conf[2], unsigned *channel_map)
+{
+ static int32_t stg1_filter_state[APP_MIC_COUNT][8];
+ static int32_t stg2_filter_state[APP_MIC_COUNT][SMALL_768K_TO_12K_FILTER_STG2_TAP_COUNT];
+ 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 = 2;
+ // filter stage 1
+ filter_conf[0].coef = (int32_t*)small_768k_to_12k_filter_stg1_coef;
+ filter_conf[0].num_taps = SMALL_768K_TO_12K_FILTER_STG1_TAP_COUNT;
+ filter_conf[0].decimation_factor = SMALL_768K_TO_12K_FILTER_STG1_DECIMATION_FACTOR;
+ filter_conf[0].state = (int32_t*)stg1_filter_state;
+ filter_conf[0].shr = SMALL_768K_TO_12K_FILTER_STG1_SHR;
+ filter_conf[0].state_words_per_channel = filter_conf[0].num_taps/32; // works on 1-bit samples
+ // filter stage 2
+ filter_conf[1].coef = (int32_t*)small_768k_to_12k_filter_stg2_coef;
+ filter_conf[1].num_taps = SMALL_768K_TO_12K_FILTER_STG2_TAP_COUNT;
+ filter_conf[1].decimation_factor = SMALL_768K_TO_12K_FILTER_STG2_DECIMATION_FACTOR;
+ filter_conf[1].state = (int32_t*)stg2_filter_state;
+ filter_conf[1].shr = SMALL_768K_TO_12K_FILTER_STG2_SHR;
+ filter_conf[1].state_words_per_channel = SMALL_768K_TO_12K_FILTER_STG2_TAP_COUNT;
+
+ // pdm rx
+ static uint32_t pdmrx_out_block[APP_MIC_COUNT][SMALL_768K_TO_12K_FILTER_STG2_DECIMATION_FACTOR];
+ static uint32_t pdmrx_out_block_double_buf[2][APP_MIC_COUNT * SMALL_768K_TO_12K_FILTER_STG2_DECIMATION_FACTOR] __attribute__((aligned(8)));
+ mic_array_conf->pdmrx_conf.pdm_out_words_per_channel = SMALL_768K_TO_12K_FILTER_STG2_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;
+}
+
+void user_mic(chanend_t c_mic_audio)
+{
+ printf("Mic Init\n");
+ device_pll_init();
+ unsigned channel_map[1] = {0};
+ mic_array_conf_t mic_array_conf;
+ mic_array_filter_conf_t filter_conf[2];
+ init_mic_conf(&mic_array_conf, filter_conf, channel_map);
+ mic_array_init_custom_filter(&pdm_res, &mic_array_conf);
+ 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;
+}
diff --git a/examples/app_mic_array_basic/src/app_config.h b/examples/app_mic_array_basic/src/app_config.h
new file mode 100644
index 00000000..83271a05
--- /dev/null
+++ b/examples/app_mic_array_basic/src/app_config.h
@@ -0,0 +1,20 @@
+// Copyright 2026 XMOS LIMITED.
+// This Software is subject to the terms of the XMOS Public Licence: Version 1.
+
+#pragma once
+
+// -------------------- Frecuency and Port definitions --------------------
+#define MIC_ARRAY_CONFIG_MCLK_FREQ (24576000) /* 24 MHz */
+#define MIC_ARRAY_CONFIG_PDM_FREQ (768000) /* 768 KHz */
+#define MIC_ARRAY_CONFIG_PORT_MCLK XS1_PORT_1D /* X0D11, J14 - Pin 15, '11' */
+#define MIC_ARRAY_CONFIG_PORT_PDM_CLK PORT_MIC_CLK /* X0D00, J14 - Pin 2, '00' */
+#define MIC_ARRAY_CONFIG_PORT_PDM_DATA PORT_MIC_DATA /* X0D14..X0D21 | J14 - Pin 3,5,12,14 and Pin 6,7,10,11 */
+#define MIC_ARRAY_CONFIG_CLOCK_BLOCK_A XS1_CLKBLK_2
+
+// ------------------------- App Definitions -----------------------------------
+#define APP_N_SAMPLES (320)
+#define APP_OUT_FREQ_HZ (12000) // 12KHz
+#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_MIC_COUNT (MIC_ARRAY_CONFIG_MIC_COUNT)
diff --git a/examples/app_mic_array_basic/src/config.xscope b/examples/app_mic_array_basic/src/config.xscope
new file mode 100644
index 00000000..d3a3da63
--- /dev/null
+++ b/examples/app_mic_array_basic/src/config.xscope
@@ -0,0 +1,18 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/examples/app_mic_array_basic/src/device_pll_ctrl.c b/examples/app_mic_array_basic/src/device_pll_ctrl.c
new file mode 100644
index 00000000..83b48726
--- /dev/null
+++ b/examples/app_mic_array_basic/src/device_pll_ctrl.c
@@ -0,0 +1,32 @@
+// Copyright 2022-2026 XMOS LIMITED.
+// This Software is subject to the terms of the XMOS Public Licence: Version 1.
+
+#include
+#include
+#include
+#include
+#include "device_pll_ctrl.h"
+
+
+void device_pll_init(void)
+{
+ unsigned tileid = get_local_tile_id();
+
+ const unsigned DEVICE_PLL_DISABLE = 0x0201FF04;
+ const unsigned DEVICE_PLL_DIV_0 = 0x80000004;
+
+ 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);
+}
diff --git a/examples/app_mic_array_basic/src/device_pll_ctrl.h b/examples/app_mic_array_basic/src/device_pll_ctrl.h
new file mode 100644
index 00000000..7fe002e6
--- /dev/null
+++ b/examples/app_mic_array_basic/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/examples/app_mic_array_basic/src/main.xc b/examples/app_mic_array_basic/src/main.xc
new file mode 100644
index 00000000..2c695375
--- /dev/null
+++ b/examples/app_mic_array_basic/src/main.xc
@@ -0,0 +1,25 @@
+// Copyright 2023-2026 XMOS LIMITED.
+// This Software is subject to the terms of the XMOS Public Licence: Version 1.
+
+#include
+#include
+#include
+#include
+
+#include
+#include
+
+extern "C" {
+ void main_tile_0();
+ void main_tile_1();
+}
+
+int main(void)
+{
+ // Initialize parallel tasks
+ par{
+ on tile[0]: main_tile_0();
+ on tile[1]: main_tile_1();
+ }
+ return 0;
+}
diff --git a/examples/app_mic_array_basic/src/small_768k_to_12k_filter.h b/examples/app_mic_array_basic/src/small_768k_to_12k_filter.h
new file mode 100644
index 00000000..867c2290
--- /dev/null
+++ b/examples/app_mic_array_basic/src/small_768k_to_12k_filter.h
@@ -0,0 +1,59 @@
+// Copyright 2026 XMOS LIMITED.
+// This Software is subject to the terms of the XMOS Public Licence: Version 1.
+
+#ifndef SMALL_768K_TO_12K_FILTER_H
+#define SMALL_768K_TO_12K_FILTER_H
+
+/* Autogenerated by running 'python combined.py small_768k_to_12k_filter_int.pkl -fp small_768k_to_12k_filter'. Do not edit */
+
+#include
+
+
+#define SMALL_768K_TO_12K_FILTER_STG1_DECIMATION_FACTOR 32
+#define SMALL_768K_TO_12K_FILTER_STG1_TAP_COUNT 256
+#define SMALL_768K_TO_12K_FILTER_STG1_SHR 0 /*shr not relevant for stage 1*/
+
+
+uint32_t small_768k_to_12k_filter_stg1_coef[128] = {
+ 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFF2DBBA, 0x1E443FC2, 0x2788F9F1, 0x1E443FC2, 0x2785DDB4,
+ 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFF86BEB, 0x1C91CEC9, 0x8DC6F6F6, 0x3B193738, 0x938D7D61,
+ 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFDBC29, 0x211BF8E9, 0x323BF6FD, 0xC4C971FD, 0x884943DB,
+ 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFE89A2, 0x721D515E, 0x02D0A650, 0xB407A8AB, 0x84E45917,
+ 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFF26BF, 0x614B35F7, 0xE678C631, 0xE67EFACD, 0x286FD64F,
+ 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFCA48, 0x0C0BC045, 0x42E8F9F1, 0x742A203D, 0x0301253F,
+ 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFF358, 0x5EE51139, 0x80C16668, 0x3019C88A, 0x77A1ACFF,
+ 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFC6D, 0x3F5E4E54, 0xAB2F696F, 0x4D52A727, 0xAFCB63FF,
+ 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFF8E, 0x553F9533, 0x994F30CF, 0x299CCA9F, 0xCAA71FFF,
+ 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFF0, 0x66554CF0, 0x78DA4025, 0xB1E0F32A, 0xA660FFFF,
+ 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0x879996A5, 0x5293801C, 0x94AA5699, 0x9E1FFFFF,
+ 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xF81E18C6, 0x631C0003, 0x8C663187, 0x81FFFFFF,
+ 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFE01F07, 0x83E00000, 0x7C1E0F80, 0x7FFFFFFF,
+ 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFE007, 0xFC000000, 0x03FE007F, 0xFFFFFFFF,
+ 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFF8, 0x00000000, 0x0001FFFF, 0xFFFFFFFF,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+};
+
+
+#define SMALL_768K_TO_12K_FILTER_STG2_DECIMATION_FACTOR 2
+#define SMALL_768K_TO_12K_FILTER_STG2_TAP_COUNT 48
+#define SMALL_768K_TO_12K_FILTER_STG2_SHR 1
+
+
+int32_t small_768k_to_12k_filter_stg2_coef[48] = {
+-0x6b2e, 0x9bb0, 0x867bf, 0x6abc3,
+-0x1d6951, -0x37fde1, 0x1b8845, 0xad6445,
+0x6737ac, -0x11a7f35, -0x1d79ea4, 0x7ee25c,
+0x3e05795, 0x27d0754, -0x49e8388, -0x834e523,
+0xb8e3a0, 0xe48a501, 0xb3d7d09, -0xe33d15c,
+-0x212034e8, -0x6b83320, 0x408190d3, 0x7fffffff,
+0x7fffffff, 0x408190d3, -0x6b83320, -0x212034e8,
+-0xe33d15c, 0xb3d7d09, 0xe48a501, 0xb8e3a0,
+-0x834e523, -0x49e8388, 0x27d0754, 0x3e05795,
+0x7ee25c, -0x1d79ea4, -0x11a7f35, 0x6737ac,
+0xad6445, 0x1b8845, -0x37fde1, -0x1d6951,
+0x6abc3, 0x867bf, 0x9bb0, -0x6b2e,
+};
+
+#define NUM_DECIMATION_STAGES (2)
+
+#endif
diff --git a/examples/app_par_decimator/src/app.cpp b/examples/app_par_decimator/src/app.cpp
index 2b3f5874..1b01f7e4 100644
--- a/examples/app_par_decimator/src/app.cpp
+++ b/examples/app_par_decimator/src/app.cpp
@@ -43,8 +43,12 @@ pdm_rx_resources_t pdm_res = PDM_RX_RESOURCES_DDR(
#define APP_N_MICS_IN APP_N_MICS
#endif
#define STAGE2_DEC_FACTOR_48KHZ 2
-#define CLRSR(c) asm volatile("clrsr %0" : : "n"(c));
-#define CLEAR_KEDI() CLRSR(XS1_SR_KEDI_MASK)
+
+#if defined(__XS3A__)
+#define CLEAR_KEDI() asm volatile("clrsr %0" : : "n"(XS1_SR_KEDI_MASK));
+#else
+#define CLEAR_KEDI() ((void)0) // not defined in !xs3a
+#endif
using TMicArray = mic_array::MicArray 0; k--) {
+ buff[k] = buff[k-1];
+ }
+ #endif
}
diff --git a/lib_mic_array/api/mic_array/cpp/Decimator.hpp b/lib_mic_array/api/mic_array/cpp/Decimator.hpp
index 06003b59..1f3b8eaf 100644
--- a/lib_mic_array/api/mic_array/cpp/Decimator.hpp
+++ b/lib_mic_array/api/mic_array/cpp/Decimator.hpp
@@ -181,5 +181,12 @@ void mic_array::shift_buffer(uint32_t* buff)
#if defined(__XS3A__)
uint32_t* src = &buff[-1];
asm volatile("vldd %0[0]; vstd %1[0];" :: "r"(src), "r"(buff) : "memory" );
- #endif // __XS3A__
+ #elif defined(__VX4B__)
+ uint32_t* src = &buff[-1];
+ asm volatile("xm.vldd %0; xm.vstd %1;" :: "r"(src), "r"(buff) : "memory" );
+ #else // C fallback
+ for (unsigned k = 7; k > 0; k--) {
+ buff[k] = buff[k-1];
+ }
+ #endif
}
diff --git a/lib_mic_array/api/mic_array/cpp/PdmRx.hpp b/lib_mic_array/api/mic_array/cpp/PdmRx.hpp
index 938b1968..5634de11 100644
--- a/lib_mic_array/api/mic_array/cpp/PdmRx.hpp
+++ b/lib_mic_array/api/mic_array/cpp/PdmRx.hpp
@@ -164,9 +164,10 @@ extern "C" {
:
: "r"(p_pdm_mics), "r"(XS1_SETC_IE_MODE_INTERRUPT)
: "r11" );
- #endif // __XS3A__
+ #else
+ #warning "PDM rx ISR not supported yet on this architecture."
+ #endif
}
-
}
diff --git a/lib_mic_array/api/mic_array/etc/xcore_compat.h b/lib_mic_array/api/mic_array/etc/xcore_compat.h
index 2d70e63d..c4c5e9b2 100644
--- a/lib_mic_array/api/mic_array/etc/xcore_compat.h
+++ b/lib_mic_array/api/mic_array/etc/xcore_compat.h
@@ -32,11 +32,9 @@ extern "C" {
#else //__XC__
-#include
#include
#include
#include
#include
#endif //__XC__
-
diff --git a/lib_mic_array/api/mic_array/impl/setup_impl.h b/lib_mic_array/api/mic_array/impl/setup_impl.h
index cf967363..1eb68780 100644
--- a/lib_mic_array/api/mic_array/impl/setup_impl.h
+++ b/lib_mic_array/api/mic_array/impl/setup_impl.h
@@ -14,4 +14,4 @@ unsigned mic_array_mclk_divider(
return master_clock_freq / pdm_clock_freq;
}
-#endif
\ No newline at end of file
+#endif
diff --git a/lib_mic_array/api/mic_array/setup.h b/lib_mic_array/api/mic_array/setup.h
index 479d2f2f..64746883 100644
--- a/lib_mic_array/api/mic_array/setup.h
+++ b/lib_mic_array/api/mic_array/setup.h
@@ -122,4 +122,4 @@ unsigned mic_array_mclk_divider(
#include "mic_array/impl/setup_impl.h"
-C_API_END
\ No newline at end of file
+C_API_END
diff --git a/lib_mic_array/lib_build_info.cmake b/lib_mic_array/lib_build_info.cmake
index b4c7e5ba..9a5cd3f2 100644
--- a/lib_mic_array/lib_build_info.cmake
+++ b/lib_mic_array/lib_build_info.cmake
@@ -1,6 +1,6 @@
set(LIB_NAME lib_mic_array)
set(LIB_VERSION 6.0.0)
-set(LIB_DEPENDENT_MODULES "lib_xcore_math(2.4.0)")
+set(LIB_DEPENDENT_MODULES "lib_xcore_math(develop)") #TODO pin version
set(LIB_INCLUDES
api
api/mic_array
diff --git a/lib_mic_array/src/deinterleave16.S b/lib_mic_array/src/deinterleave16.S
index efebb43e..2860f442 100644
--- a/lib_mic_array/src/deinterleave16.S
+++ b/lib_mic_array/src/deinterleave16.S
@@ -102,7 +102,7 @@ deinterleave16:
std f, b, x[6]
std h, d, x[7]
-
+ // part2
ldd a, b, x[0]
ldd c, d, x[4]
unzip b, d, 0
@@ -143,3 +143,127 @@ deinterleave16:
.size deinterleave16, .L_end - deinterleave16
#endif // __XS3A__
+
+#if defined(__VX4A__) || defined(__VX4B__)
+
+#define FUNCTION_NAME deinterleave16
+#define NSTACK_WORDS 8
+#define NSTACK_BYTES (NSTACK_WORDS*4)
+
+#define x a0
+#define a a1
+#define b a2
+
+#define c s2
+#define d s3
+#define e s4
+#define f s5
+#define g s6
+#define h s7
+
+// Note: ldd and std are reversed in vx4
+
+.p2align 4
+.globl FUNCTION_NAME
+.type FUNCTION_NAME,@function
+FUNCTION_NAME:
+ // save regs
+ xm.entsp NSTACK_BYTES
+ xm.stdsp s3,s2,0
+ xm.stdsp s5,s4,8
+ xm.stdsp s7,s6,16
+
+ // Lower half
+ xm.ldd b, a, (8*3)(x)
+ xm.ldd d, c, (8*2)(x)
+ xm.ldd f, e, (8*1)(x)
+ xm.ldd h, g, (8*0)(x)
+
+ xm.unzip b, a, 2
+ xm.unzip d, c, 2
+ xm.unzip f, e, 2
+ xm.unzip h, g, 2
+
+ xm.unzip c, a, 1
+ xm.unzip d, b, 1
+ xm.unzip g, e, 1
+ xm.unzip h, f, 1
+
+ xm.unzip e, a, 0
+ xm.unzip f, b, 0
+ xm.unzip g, c, 0
+ xm.unzip h, d, 0
+
+ xm.std a, e, (8*0)(x)
+ xm.std c, g, (8*1)(x)
+ xm.std b, f, (8*2)(x)
+ xm.std d, h, (8*3)(x)
+
+ // Upper half
+ xm.ldd b, a, (8*7)(x)
+ xm.ldd d, c, (8*6)(x)
+ xm.ldd f, e, (8*5)(x)
+ xm.ldd h, g, (8*4)(x)
+
+ xm.unzip b, a, 2
+ xm.unzip d, c, 2
+ xm.unzip f, e, 2
+ xm.unzip h, g, 2
+
+ xm.unzip c, a, 1
+ xm.unzip d, b, 1
+ xm.unzip g, e, 1
+ xm.unzip h, f, 1
+
+ xm.unzip e, a, 0
+ xm.unzip f, b, 0
+ xm.unzip g, c, 0
+ xm.unzip h, d, 0
+
+ xm.std a, e, (8*4)(x)
+ xm.std c, g, (8*5)(x)
+ xm.std b, f, (8*6)(x)
+ xm.std d, h, (8*7)(x)
+
+ // part2
+ xm.ldd b, a, (8*0)(x)
+ xm.ldd d, c, (8*4)(x)
+ xm.unzip b, d, 0
+ xm.unzip a, c, 0
+ xm.std b, a, (8*4)(x)
+ xm.std d, c, (8*0)(x)
+
+ xm.ldd b, a, (8*1)(x)
+ xm.ldd d, c, (8*5)(x)
+ xm.unzip b, d, 0
+ xm.unzip a, c, 0
+ xm.std b, a, (8*5)(x)
+ xm.std d, c, (8*1)(x)
+
+ xm.ldd b, a, (8*2)(x)
+ xm.ldd d, c, (8*6)(x)
+ xm.unzip b, d, 0
+ xm.unzip a, c, 0
+ xm.std b, a, (8*6)(x)
+ xm.std d, c, (8*2)(x)
+
+ xm.ldd b, a, (8*3)(x)
+ xm.ldd d, c, (8*7)(x)
+ xm.unzip b, d, 0
+ xm.unzip a, c, 0
+ xm.std b, a, (8*7)(x)
+ xm.std d, c, (8*3)(x)
+
+ // restore regs
+ xm.lddsp s3,s2,0
+ xm.lddsp s5,s4,8
+ xm.lddsp s7,s6,16
+ xm.retsp NSTACK_BYTES
+
+.size FUNCTION_NAME, . -FUNCTION_NAME
+.resource_const FUNCTION_NAME, "stack_frame_bytes", NSTACK_BYTES
+.resource_list_empty FUNCTION_NAME, "callees"
+.resource_list_empty FUNCTION_NAME, "tail_callees"
+.resource_list_empty FUNCTION_NAME, "parallel_callees"
+
+#endif
diff --git a/lib_mic_array/src/deinterleave2.S b/lib_mic_array/src/deinterleave2.S
index 6c08f352..923c5461 100644
--- a/lib_mic_array/src/deinterleave2.S
+++ b/lib_mic_array/src/deinterleave2.S
@@ -41,3 +41,28 @@ deinterleave2:
.size deinterleave2, .L_end - deinterleave2
#endif // __XS3A__
+
+#if defined(__VX4A__) || defined(__VX4B__)
+
+#define FUNCTION_NAME deinterleave2
+#define NSTACK_BYTES 16 // minimum
+
+// Note: ldd and std are reversed in vx4
+
+.p2align 1
+.globl FUNCTION_NAME
+.type FUNCTION_NAME,@function
+FUNCTION_NAME:
+ xm.entsp NSTACK_BYTES
+ xm.ldd a2, a1, 0(a0)
+ xm.unzip a2, a1, 0
+ xm.std a1, a2, 0(a0)
+ xm.retsp NSTACK_BYTES
+
+.size FUNCTION_NAME, . -FUNCTION_NAME
+.resource_const FUNCTION_NAME, "stack_frame_bytes", NSTACK_BYTES
+.resource_list_empty FUNCTION_NAME, "callees"
+.resource_list_empty FUNCTION_NAME, "tail_callees"
+.resource_list_empty FUNCTION_NAME, "parallel_callees"
+
+#endif // __VX4A__ || __VX4B__
diff --git a/lib_mic_array/src/deinterleave4.S b/lib_mic_array/src/deinterleave4.S
index 0d383e9c..5a4a8ef9 100644
--- a/lib_mic_array/src/deinterleave4.S
+++ b/lib_mic_array/src/deinterleave4.S
@@ -85,3 +85,49 @@ deinterleave4:
.size deinterleave4, .L_end - deinterleave4
#endif // __XS3A__
+
+#if defined(__VX4A__) || defined(__VX4B__)
+
+#define FUNCTION_NAME deinterleave4
+#define NSTACK_WORDS 4
+#define NSTACK_BYTES (NSTACK_WORDS*4)
+
+#define x a0
+#define a a1
+#define b a2
+#define c s2
+#define d s3
+
+// Note: ldd and std are reversed in vx4
+
+.p2align 1
+.globl FUNCTION_NAME
+.type FUNCTION_NAME,@function
+FUNCTION_NAME:
+ xm.entsp NSTACK_BYTES
+ xm.stdsp s2, s3, 0*8
+
+ // Save and Load
+ xm.ldd b, a, 8(a0)
+ xm.ldd d, c, 0(a0)
+
+ // Deinterleave
+ xm.unzip b, a, 1
+ xm.unzip d, c, 1
+ xm.unzip c, a, 0
+ xm.unzip d, b, 0
+
+ // Store and Restore regs
+ xm.std a, c, 0(a0)
+ xm.std b, d, 8(a0)
+
+ xm.lddsp s2, s3, 0*8
+ xm.retsp NSTACK_BYTES
+
+.size FUNCTION_NAME, . -FUNCTION_NAME
+.resource_const FUNCTION_NAME, "stack_frame_bytes", NSTACK_BYTES
+.resource_list_empty FUNCTION_NAME, "callees"
+.resource_list_empty FUNCTION_NAME, "tail_callees"
+.resource_list_empty FUNCTION_NAME, "parallel_callees"
+
+#endif
diff --git a/lib_mic_array/src/deinterleave8.S b/lib_mic_array/src/deinterleave8.S
index c3d6a955..cc9a876c 100644
--- a/lib_mic_array/src/deinterleave8.S
+++ b/lib_mic_array/src/deinterleave8.S
@@ -115,3 +115,73 @@ deinterleave8:
.size deinterleave8, .L_end - deinterleave8
#endif // __XS3A__
+
+
+#if defined(__VX4A__) || defined(__VX4B__)
+
+#define FUNCTION_NAME deinterleave8
+#define NSTACK_WORDS 8
+#define NSTACK_BYTES (NSTACK_WORDS*4)
+
+// Note: ldd and std are reversed in vx4
+
+#define x a0
+#define a a1
+#define b a2
+
+#define c s2
+#define d s3
+#define e s4
+#define f s5
+#define g s6
+#define h s7
+
+.p2align 1
+.globl FUNCTION_NAME
+.type FUNCTION_NAME,@function
+FUNCTION_NAME:
+ // save regs
+ xm.entsp NSTACK_BYTES
+ xm.stdsp c, d, 0*8
+ xm.stdsp e, f, 1*8
+ xm.stdsp g, h, 2*8
+
+ // deinterleave
+ xm.ldd b, a, 24(x)
+ xm.ldd d, c, 16(x)
+ xm.ldd f, e, 8(x)
+ xm.ldd h, g, 0(x)
+
+ xm.unzip b, a, 2
+ xm.unzip d, c, 2
+ xm.unzip f, e, 2
+ xm.unzip h, g, 2
+
+ xm.unzip c, a, 1
+ xm.unzip d, b, 1
+ xm.unzip g, e, 1
+ xm.unzip h, f, 1
+
+ xm.unzip e, a, 0
+ xm.unzip f, b, 0
+ xm.unzip g, c, 0
+ xm.unzip h, d, 0
+
+ xm.std a, e, 0(a0)
+ xm.std c, g, 8(a0)
+ xm.std b, f, 16(a0)
+ xm.std d, h, 24(a0)
+
+ // restore regs
+ xm.lddsp c, d, 0*8
+ xm.lddsp e, f, 1*8
+ xm.lddsp g, h, 2*8
+ xm.retsp NSTACK_BYTES
+
+.size FUNCTION_NAME, . -FUNCTION_NAME
+.resource_const FUNCTION_NAME, "stack_frame_bytes", NSTACK_BYTES
+.resource_list_empty FUNCTION_NAME, "callees"
+.resource_list_empty FUNCTION_NAME, "tail_callees"
+.resource_list_empty FUNCTION_NAME, "parallel_callees"
+
+#endif // __VX4A__ || __VX4B__
diff --git a/lib_mic_array/src/fir_1x16_bit.S b/lib_mic_array/src/fir_1x16_bit.S
index 576d4ef9..1033d6a1 100644
--- a/lib_mic_array/src/fir_1x16_bit.S
+++ b/lib_mic_array/src/fir_1x16_bit.S
@@ -72,3 +72,68 @@ macc_coeffs:
.cc_bottom fir_1x16_bit.func
#endif // __XS3A__
+
+
+#if defined(__VX4A__) || defined(__VX4B__)
+
+/**
+ * This function is the optimal FIR on a 1-bit signal with 16-bit coefficients.
+ *
+ * NOTE: This version is optimized for the mic array and takes only a single block of coefficients
+ *
+ * r0: argument 1, signal (word aligned)
+ * r1: argument 2, coefficients (arranged as 16 1-bit arrays, word aligned)
+ * r2: spare
+ * r3: spare
+ * r11: spare
+*/
+
+#define FUNCTION_NAME fir_1x16_bit
+#define NSTACK_WORDS 16
+#define NSTACK_BYTES (NSTACK_WORDS*4)
+
+.p2align 4
+.globl FUNCTION_NAME
+.type FUNCTION_NAME,@function
+FUNCTION_NAME:
+ { li a3, 32 ; xm.entsp NSTACK_BYTES}
+ { slli t3, a3, 3 ; xm.vclrdr}
+ { xm.nop ; xm.vsetc t3}
+ { xm.nop ; xm.vldc a0}
+ { add a1, a1, a3 ; xm.vlmaccrb a1}
+ { add a1, a1, a3 ; xm.vlmaccrb a1}
+ { add a1, a1, a3 ; xm.vlmaccrb a1}
+ { add a1, a1, a3 ; xm.vlmaccrb a1}
+ { add a1, a1, a3 ; xm.vlmaccrb a1}
+ { add a1, a1, a3 ; xm.vlmaccrb a1}
+ { add a1, a1, a3 ; xm.vlmaccrb a1}
+ { add a1, a1, a3 ; xm.vlmaccrb a1}
+ { add a1, a1, a3 ; xm.vlmaccrb a1}
+ { add a1, a1, a3 ; xm.vlmaccrb a1}
+ { add a1, a1, a3 ; xm.vlmaccrb a1}
+ { add a1, a1, a3 ; xm.vlmaccrb a1}
+ { add a1, a1, a3 ; xm.vlmaccrb a1}
+ { add a1, a1, a3 ; xm.vlmaccrb a1}
+ { add a1, a1, a3 ; xm.vlmaccrb a1}
+ { addi t3,sp, 0 ; xm.vlmaccrb a1}
+ //TODO Below we could save max of 2 cycles?
+ xm.vstr t3
+ { xm.vclrdr; addi a2, sp, 0}
+ xm.vldc t3
+ xm.ldap t3, macc_coeffs
+ xm.vlmaccr0 t3
+ xm.vlmaccr1 t3
+ { addi a2, a2, 4 ; xm.vstr a2}
+ xm.vstd a2
+ xm.lddsp a0, a1, 0
+ xm.zip a1, a0, 4
+ slli a0, a0, 8
+ xm.retsp NSTACK_BYTES
+
+// The order of these coefficients tells us that whatever gets VLMACCR1'ed last is going to be multiplied by
+// the largest coefficient. Thus, if the bipolar coefficient matrix B[,] has shape 16x32, then B[0,:] must
+// correspond to the LEAST significant bits of each coefficient
+macc_coeffs:
+ .short 0x7fff, 0x4000, 0x2000, 0x1000, 0x0800, 0x0400, 0x0200, 0x0100, 0x0080, 0x0040, 0x0020, 0x0010, 0x0008, 0x0004, 0x0002, 0x0001
+
+#endif // __VX4A__ || __VX4B__
diff --git a/lib_mic_array/src/mic_array_setup.c b/lib_mic_array/src/mic_array_setup.c
index 3a9ceac6..2bb4a136 100644
--- a/lib_mic_array/src/mic_array_setup.c
+++ b/lib_mic_array/src/mic_array_setup.c
@@ -48,11 +48,16 @@ void mic_array_resources_configure(
static inline
void mic_array_inpw8(const port_t p_pdm_mics)
{
- #if defined(__XS3A__)
uint32_t tmp;
+ #if defined(__XS3A__)
asm volatile("inpw %0, res[%1], 8" : "=r"(tmp)
: "r" (p_pdm_mics));
- #endif // __XS3A__
+ #elif defined(__VX4B__)
+ asm volatile("xm.inpw %0, %1, 8": "=r"(tmp): "r"(p_pdm_mics));
+ #else
+ #warning "mic_array_inpw8 not supported yet on this architecture."
+ (void) tmp;
+ #endif
}
void mic_array_pdm_clock_start(
diff --git a/lib_mic_array/src/mic_array_task.cpp b/lib_mic_array/src/mic_array_task.cpp
index 962ac90a..3e10d4bd 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 //
////////////////////
@@ -105,12 +105,10 @@ void default_ma_task_start_decimator_3stg(TMicArray_3stg_decimator& mics, chanen
}
#if defined(__XS3A__)
-#define CLRSR(c) asm volatile("clrsr %0" : : "n"(c));
+#define CLEAR_KEDI() asm volatile("clrsr %0" : : "n"(XS1_SR_KEDI_MASK));
#else
-#define CLRSR(c) ((void)0)
-#warning "CLRSR not defined for this architecture."
+#define CLEAR_KEDI() ((void)0) // not defined in !xs3a
#endif
-#define CLEAR_KEDI() CLRSR(XS1_SR_KEDI_MASK)
template
void start_mics_with_pdm_isr(TMics* mics_ptr, chanend_t c_frames_out)
@@ -171,4 +169,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/lib_mic_array/src/pdm_rx_isr.S b/lib_mic_array/src/pdm_rx_isr.S
index 96c61243..6c649f1e 100644
--- a/lib_mic_array/src/pdm_rx_isr.S
+++ b/lib_mic_array/src/pdm_rx_isr.S
@@ -109,3 +109,24 @@ pdm_rx_isr:
.global pdm_rx_isr
#endif //defined(__XS3A__)
+
+#if defined(__VX4A__) || defined(__VX4B__)
+
+#define FUNCTION_NAME pdm_rx_isr
+#define NSTACK_BYTES 16 // minimum
+
+.p2align 1
+.globl FUNCTION_NAME
+.type FUNCTION_NAME,@function
+FUNCTION_NAME:
+ xm.entsp NSTACK_BYTES
+ unimp //TODO unimplemented
+ xm.retsp NSTACK_BYTES
+
+.size FUNCTION_NAME, . -FUNCTION_NAME
+.resource_const FUNCTION_NAME, "stack_frame_bytes", NSTACK_BYTES
+.resource_list_empty FUNCTION_NAME, "callees"
+.resource_list_empty FUNCTION_NAME, "tail_callees"
+.resource_list_empty FUNCTION_NAME, "parallel_callees"
+
+#endif // __VX4A__ || __VX4B__
diff --git a/tests/signal/BasicMicArray/CMakeLists.txt b/tests/signal/BasicMicArray/CMakeLists.txt
index 97a1e98f..ca69a493 100644
--- a/tests/signal/BasicMicArray/CMakeLists.txt
+++ b/tests/signal/BasicMicArray/CMakeLists.txt
@@ -4,9 +4,26 @@ project(test_ma)
set(XMOS_SANDBOX_DIR ${CMAKE_CURRENT_LIST_DIR}/../../../..)
-include(${CMAKE_CURRENT_LIST_DIR}/../../../examples/deps.cmake)
-
-set(APP_HW_TARGET XK-EVK-XU316)
+set(APP_DEPENDENT_MODULES "lib_mic_array")
+
+# conditional depending on target
+if(CMAKE_C_COMPILER_VERSION VERSION_EQUAL "3.6.0") # XS3 (XTC 15.3.1)
+ set(APP_HW_TARGET XK-EVK-XU316)
+ set(COMMON_COMPILER_FLAGS -O2
+ -g
+ -report
+ -mcmodel=large
+ -Wno-xcore-fptrgroup
+ -Wno-unknown-pragmas
+ -Wno-format)
+
+else() # VX4
+ set(APP_HW_TARGET XK-EVK-XU416)
+ set(COMMON_COMPILER_FLAGS -Os
+ -g
+ -Wno-fptrgroup
+ -Wno-format)
+endif()
set_property(DIRECTORY "${CMAKE_CURRENT_LIST_DIR}" PROPERTY CMAKE_CONFIGURE_DEPENDS
"${CMAKE_CURRENT_LIST_DIR}/test_params.json")
@@ -84,13 +101,7 @@ foreach(l RANGE 0 ${NUM_SAMP_FREQ})
set(CONFIG "${N_MICS}ch_${FRAME_SIZE}smp_${USE_ISR}isr_${samp_freq_str}")
message(${CONFIG})
- set(APP_COMPILER_FLAGS_${CONFIG} -O2
- -g
- -report
- -mcmodel=large
- -Wno-xcore-fptrgroup
- -Wno-unknown-pragmas
- -Wno-format
+ set(APP_COMPILER_FLAGS_${CONFIG} ${COMMON_COMPILER_FLAGS}
-DMIC_ARRAY_CONFIG_USE_PDM_ISR=${USE_ISR}
-DMIC_ARRAY_CONFIG_SAMPLES_PER_FRAME=${FRAME_SIZE}
-DMIC_ARRAY_CONFIG_MIC_COUNT=${N_MICS}
@@ -113,4 +124,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..255b067d 100644
--- a/tests/signal/BasicMicArray/src/app.cpp
+++ b/tests/signal/BasicMicArray/src/app.c
@@ -1,30 +1,45 @@
// 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_config.h"
-#include "app.h"
#if USE_CUSTOM_FILTER
#include "custom_filter.h"
#endif
+#ifndef META_OUT
+#define META_OUT (0)
+#endif
+
+#ifndef DATA_OUT
+#define DATA_OUT (1)
+#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 +54,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 +116,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 +230,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 +268,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 +287,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 +342,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 +359,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/app_config.h b/tests/signal/BasicMicArray/src/app_config.h
new file mode 100644
index 00000000..9d0e7644
--- /dev/null
+++ b/tests/signal/BasicMicArray/src/app_config.h
@@ -0,0 +1,22 @@
+// Copyright 2026 XMOS LIMITED.
+// This Software is subject to the terms of the XMOS Public Licence: Version 1.
+
+#pragma once
+
+#if defined(__VX4B__)
+
+#include
+
+#ifndef PORT_MCLK_IN
+#define PORT_MCLK_IN XS1_PORT_1D
+#endif
+
+#ifndef PORT_PDM_CLK
+#define PORT_PDM_CLK XS1_PORT_1G
+#endif
+
+#ifndef PORT_PDM_DATA
+#define PORT_PDM_DATA XS1_PORT_1F
+#endif
+
+#endif
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;
-}
-
-}
diff --git a/tests/signal/BasicMicArray/test_params.json b/tests/signal/BasicMicArray/test_params.json
index cdca885c..33755163 100644
--- a/tests/signal/BasicMicArray/test_params.json
+++ b/tests/signal/BasicMicArray/test_params.json
@@ -1,6 +1,6 @@
{
- "N_MICS": [1, 2, 4, 8],
- "FRAME_SIZE": [1, 16],
- "USE_ISR": [0, 1],
- "SAMP_FREQ": [16000, 32000, 48000, "good_3_stage_filter_int.pkl"]
-}
+ "N_MICS": [1],
+ "FRAME_SIZE": [16],
+ "USE_ISR": [0],
+ "SAMP_FREQ": [16000]
+}
\ No newline at end of file
diff --git a/tests/signal/profile/app_memory/src/app.cpp b/tests/signal/profile/app_memory/src/app.cpp
index 77c7e465..69df901a 100644
--- a/tests/signal/profile/app_memory/src/app.cpp
+++ b/tests/signal/profile/app_memory/src/app.cpp
@@ -50,8 +50,12 @@ pdm_rx_resources_t pdm_res = PDM_RX_RESOURCES_DDR(
#ifndef APP_N_MICS_IN
#define APP_N_MICS_IN APP_N_MICS
#endif
-#define CLRSR(c) asm volatile("clrsr %0" : : "n"(c));
-#define CLEAR_KEDI() CLRSR(XS1_SR_KEDI_MASK)
+
+#if defined(__XS3A__)
+#define CLEAR_KEDI() asm volatile("clrsr %0" : : "n"(XS1_SR_KEDI_MASK));
+#else
+#define CLEAR_KEDI() ((void)0) // not defined in !xs3a
+#endif
using TMicArray = mic_array::MicArray,
@@ -135,4 +139,3 @@ void app_mic_array_task(chanend_t c_frames_out)
#endif
}
#endif
-
diff --git a/tests/unit/CMakeLists.txt b/tests/unit/CMakeLists.txt
index 557f0416..9352cc9a 100644
--- a/tests/unit/CMakeLists.txt
+++ b/tests/unit/CMakeLists.txt
@@ -4,10 +4,20 @@ include($ENV{XMOS_CMAKE_PATH}/xcommon.cmake)
project(tests-unit)
set(XMOS_SANDBOX_DIR ${CMAKE_CURRENT_LIST_DIR}/../../..)
-set(APP_HW_TARGET XK-EVK-XU316)
set(APP_INCLUDES src)
-set(APP_DEPENDENT_MODULES "lib_mic_array" "lib_unity(2.5.2)")
-set(APP_COMPILER_FLAGS -O2
+set(APP_DEPENDENT_MODULES "lib_mic_array" "lib_unity(main)") #TODO release lib_unity
+
+# conditional depending on target
+if(CMAKE_C_COMPILER_VERSION VERSION_EQUAL "3.6.0")
+ set(__XS3__ ON) # XS3 (XTC 15.3.1)
+else()
+ set(__XS3__ OFF) # VX4
+endif()
+
+# Target specific compiler flags
+if(__XS3__) # xs3
+ set(APP_HW_TARGET XK-EVK-XU316)
+ set(APP_COMPILER_FLAGS -O2
-g
-report
-mcmodel=large
@@ -17,5 +27,13 @@ set(APP_COMPILER_FLAGS -O2
-Wno-format
-fxscope
-DUNITY_INCLUDE_CONFIG_H=1)
+else() # vx4
+ set(APP_HW_TARGET XK-EVK-XU416)
+ set(APP_COMPILER_FLAGS
+ -Os
+ -g
+ -Wno-fptrgroup
+ -DUNITY_INCLUDE_CONFIG_H=1)
+endif()
XMOS_REGISTER_APP()
diff --git a/tests/unit/src/main.c b/tests/unit/src/main.c
index 88172bff..849766a9 100644
--- a/tests/unit/src/main.c
+++ b/tests/unit/src/main.c
@@ -8,7 +8,6 @@
int main(int argc, const char* argv[])
{
- xscope_config_io(XSCOPE_IO_BASIC);
UnityGetCommandLineOptions(argc, argv);
UnityBegin(argv[0]);
@@ -28,8 +27,8 @@ int main(int argc, const char* argv[])
RUN_TEST_GROUP(deinterleave4);
RUN_TEST_GROUP(deinterleave8);
RUN_TEST_GROUP(deinterleave16);
-
RUN_TEST_GROUP(deinterleave_pdm_samples);
-
+ RUN_TEST_GROUP(fir_1x16_bit);
+
return UNITY_END();
}
diff --git a/tests/unit/src/test_fir_1x16_bit.c b/tests/unit/src/test_fir_1x16_bit.c
new file mode 100644
index 00000000..3916646c
--- /dev/null
+++ b/tests/unit/src/test_fir_1x16_bit.c
@@ -0,0 +1,102 @@
+// Copyright 2026 XMOS LIMITED.
+// This Software is subject to the terms of the XMOS Public Licence: Version 1.
+#include
+#include
+#include
+#include
+
+#include
+
+#include "unity.h"
+#include "unity_fixture.h"
+
+#include "mic_array/etc/fir_1x16_bit.h"
+#include "mic_array/etc/filters_default.h"
+
+TEST_GROUP_RUNNER(fir_1x16_bit) {
+ RUN_TEST_CASE(fir_1x16_bit, symmetry_test);
+ RUN_TEST_CASE(fir_1x16_bit, single_val);
+ RUN_TEST_CASE(fir_1x16_bit, random_test);
+}
+
+TEST_GROUP(fir_1x16_bit);
+TEST_SETUP(fir_1x16_bit) {}
+TEST_TEAR_DOWN(fir_1x16_bit) {}
+
+// Test that opposite signals produce opposite results
+TEST(fir_1x16_bit, symmetry_test)
+{
+ uint32_t signal_pos[1024];
+ uint32_t signal_neg[1024];
+
+ // Using real stage 1 coefficients
+ extern uint32_t stage1_coef[STAGE1_WORDS];
+
+ memset(signal_pos, 0x00, sizeof(signal_pos)); // All +1
+ memset(signal_neg, 0xFF, sizeof(signal_neg)); // All -1
+
+ int result_pos = fir_1x16_bit(signal_pos, stage1_coef);
+ int result_neg = fir_1x16_bit(signal_neg, stage1_coef);
+
+ // Opposite signals should give opposite results
+ TEST_ASSERT_EQUAL_INT(-result_pos, result_neg);
+}
+
+// Test zero signal with known inputs/outputs
+TEST(fir_1x16_bit, single_val)
+{
+ const int expected_result = 268435456;
+ const unsigned max_cycles = 35;
+
+ unsigned elapsed = 0;
+ int result = -1;
+ uint32_t signal[1024];
+ memset(signal, 0, sizeof(signal));
+
+ elapsed = get_reference_time();
+ result = fir_1x16_bit(signal, stage1_coef);
+ elapsed = get_reference_time() - elapsed;
+
+ TEST_ASSERT_EQUAL_INT(expected_result, result);
+ TEST_ASSERT_LESS_OR_EQUAL(max_cycles, elapsed);
+}
+
+TEST(fir_1x16_bit, random_test)
+{
+ #define n_vpu 16
+ #define sig_len (n_vpu * 20)
+ #define PRINT_OUT (1)
+
+ const int sig_exp[n_vpu] = {
+ -58529792,34287616,70240256,17392640,52816384,
+ -51980800,54905856,40349696,-60945408,14667776,
+ -3800064,33825280,-1670656,879616,-23246848,-11620864,
+ };
+
+ uint32_t sig_in[sig_len] = {0};
+ int sig_out[n_vpu] = {0};
+
+ // seed
+ srand(12345);
+ for (unsigned i = 0; i < sig_len; i++)
+ {
+ sig_in[i] = rand() & 0xFFFFFFFF; // Random 32-bit word
+ }
+
+ // Using real stage 1 coefficients
+ for (unsigned i = 0; i < n_vpu; i++)
+ {
+ uint32_t *sig_ptr = &sig_in[i * 20]; // 20 words per VPU block
+ sig_out[i] = fir_1x16_bit(sig_ptr, stage1_coef);
+ }
+
+ #if PRINT_OUT
+ printf("\nExpected vs Actual:\n");
+ for (unsigned i = 0; i < n_vpu; i++)
+ {
+ printf("sig_out[%u] = %d, sig_exp = %d\n", i, sig_out[i], sig_exp[i]);
+ }
+ #endif
+
+ TEST_ASSERT_EQUAL_INT_ARRAY(sig_exp, sig_out, n_vpu);
+}