This commit is contained in:
Steven Dan
2025-12-11 09:43:42 +08:00
commit d8b2974133
1822 changed files with 280037 additions and 0 deletions

View File

@@ -0,0 +1,38 @@
cmake_minimum_required(VERSION 3.21)
include($ENV{XMOS_CMAKE_PATH}/xcommon.cmake)
project(app_td_block_fir)
set(APP_HW_TARGET XK-EVK-XU316)
set(APP_DEPENDENT_MODULES
"lib_audio_dsp"
"lib_logging(3.2.0)"
"lib_locks(2.2.0)"
)
set(APP_PCA_ENABLE OFF)
set(EXAMPLE_BUILD_FLAGS ${EXTRA_BUILD_FLAGS} -fcomment-asm
-Wall
-O3
-report
-lquadflash
-mcmodel=large
-g
-fxscope)
set(APP_COMPILER_FLAGS ${EXAMPLE_BUILD_FLAGS})
file(GLOB C_SRC CONFIGURE_DEPENDS RELATIVE ${CMAKE_CURRENT_LIST_DIR} src/*.c)
set(DSP_DIR build/dsp_pipeline)
set(APP_C_SRCS
"${C_SRC};${DSP_MAIN}")
set(APP_INCLUDES
src
src/core
src/extensions
${CMAKE_CURRENT_LIST_DIR}/build/dsp_pipeline)
set(XMOS_SANDBOX_DIR ${CMAKE_CURRENT_LIST_DIR}/../../..)
XMOS_REGISTER_APP()

View File

@@ -0,0 +1,27 @@
app_fd_block_fir
---
This demonstrates the usage of a frequency domain FIR. It runs a 4096 tap
bandpass filter with a 256 sample latency. This requires around 3x less
compute than the equivalent block time domain filter, and around 6x less
compute than a single sample implementation (both VPU optimised).
To build the example, first generate the test filter `test_0` by running `make_test_filters.py`:
.. code-block:: console
cd examples/app_fd_block_fir/src
python make_test_filters.py
Then autogenerate the frequency domain filter with a frame advance of 256 samples:
.. code-block:: console
python -m audio_dsp.dsp.fd_block_fir test_0.npy 256
Finally, build the example app:
.. code-block:: console
cd ..
cmake -G "Unix Makefiles" -B build
cmake -C build

View File

@@ -0,0 +1,43 @@
// Copyright 2024-2025 XMOS LIMITED.
// This Software is subject to the terms of the XMOS Public Licence: Version 1.
#include <stdint.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <xcore/hwtimer.h>
#include "test_0.h"
void foo(){
int32_t __attribute__((aligned (8))) new_data[test_0_TD_BLOCK_LENGTH];
//allocate a TD FIR for the example
int32_t data[test_0_DATA_BUFFER_ELEMENTS];
memset(data, 0, sizeof(data));
fd_fir_data_t fd_fir_data_test_0;
fd_block_fir_data_init(&fd_fir_data_test_0, data,
test_0_FRAME_ADVANCE,
test_0_TD_BLOCK_LENGTH,
test_0_BLOCK_COUNT);
for(int j=0;j<16;j++)
{
for(int i=0;i<test_0_FRAME_ADVANCE;i++)
new_data[i] = rand()-rand();
int32_t __attribute__((aligned (8))) fd_processed[test_0_TD_BLOCK_LENGTH] = {0};
fd_block_fir_add_data(new_data, &fd_fir_data_test_0);
fd_block_fir_compute(
fd_processed,
&fd_fir_data_test_0,
&fd_fir_filter_test_0);
}
}
int main() {
foo();
}

View File

@@ -0,0 +1,13 @@
# Copyright 2024-2025 XMOS LIMITED.
# This Software is subject to the terms of the XMOS Public Licence: Version 1.
import numpy as np
from scipy import signal
def bandpass_filters(length, count):
for i in range(count):
t = signal.firwin(length, (i + 1) / (count + 2))
name = 'test_' + str(i)
np.save(name, t)
if __name__ == '__main__':
bandpass_filters(4096, 1)

View File

@@ -0,0 +1,42 @@
cmake_minimum_required(VERSION 3.21)
include($ENV{XMOS_CMAKE_PATH}/xcommon.cmake)
project(app_simple_audio_dsp_integration)
set(APP_HW_TARGET XK-EVK-XU316)
set(APP_DEPENDENT_MODULES
"lib_audio_dsp"
"lib_logging(3.2.0)"
"lib_locks(2.2.0)"
)
set(APP_PCA_ENABLE OFF)
set(EXAMPLE_BUILD_FLAGS ${EXTRA_BUILD_FLAGS} -fcomment-asm
-Wall
-O3
-report
-lquadflash
-g
-fxscope)
set(APP_COMPILER_FLAGS ${EXAMPLE_BUILD_FLAGS})
file(GLOB C_SRC CONFIGURE_DEPENDS RELATIVE ${CMAKE_CURRENT_LIST_DIR} src/*.c)
set(DSP_DIR build/dsp_pipeline)
set(DSP_MAIN ${DSP_DIR}/adsp_generated_auto.c)
if(NOT EXISTS ${DSP_MAIN})
write_file(${DSP_MAIN} "#error Generate pipeline before building or build with dsp_pipeline.ipynb")
write_file(${DSP_DIR}/adsp_generated_auto.h "#error Generate pipeline before building or build with dsp_pipeline.ipynb")
endif()
set(APP_C_SRCS
"${C_SRC};${DSP_MAIN}")
set(APP_INCLUDES
src
src/core
src/extensions
${CMAKE_CURRENT_LIST_DIR}/build/dsp_pipeline)
set(XMOS_SANDBOX_DIR ${CMAKE_CURRENT_LIST_DIR}/../../..)
XMOS_REGISTER_APP()

View File

@@ -0,0 +1,132 @@
{
"cells": [
{
"cell_type": "markdown",
"id": "ccc61334-38ac-4c86-a86a-bd2975d5a60d",
"metadata": {},
"source": [
"# lib_audio_dsp pipeline designer\n",
"\n",
"In this file you can generate the DSP pipeline of your choice.\n",
"\n",
"Below you will find 3 cells which can be modified and executed to configure, tune and run the desired pipeline.\n",
"\n"
]
},
{
"cell_type": "markdown",
"id": "0acbc09d",
"metadata": {},
"source": [
"1. This is the pipeline design cell. Here you must break the DSP pipeline down into threads and use the provided DSP stages to create a pipeline. Running this cell will produce a diagram showing your pipeline. Make sure to capture each stage in your pipeline as a variable, as it will be needed in the next step.\n",
"Note that every time the pipeline cell is changed, the app must be regenerated before the tuning stage can work correctly as the stage indices used for communication may have changed."
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "f3806bd0-99e0-42b6-a084-e9a2f17ba7dc",
"metadata": {},
"outputs": [],
"source": [
"# Pipeline design stage\n",
"\n",
"from audio_dsp.design.pipeline import Pipeline\n",
"from audio_dsp.stages import *\n",
"\n",
"p, inputs = Pipeline.begin(1, fs=48000)\n",
"\n",
"# i is a list of pipeline inputs. \"lowshelf\" is a label for this instance of Biquad.\n",
"# The new variable x is the output of the lowshelf Biquad\n",
"x = p.stage(Biquad, inputs, \"lowshelf\")\n",
"\n",
"# The output of lowshelf \"x\" is passed as the input to the\n",
"# highshelf. The variable x is reassigned to the outputs of the new Biquad.\n",
"x = p.stage(Biquad, x, \"highshelf\")\n",
"\n",
"# Connect highshelf to the limiter. Labels are optional, however they are required\n",
"# if the stage will be tuned later.\n",
"x = p.stage(LimiterPeak, x)\n",
"\n",
"# Finally connect to the output of the pipeline.\n",
"p.set_outputs(x)\n",
"\n",
"p.draw()\n"
]
},
{
"cell_type": "markdown",
"id": "27e9d385",
"metadata": {},
"source": [
"2. This is the tuning cell. First time through this can be ignored, but once your pipeline is running on a connected device, this cell can be updated and executed to update each pipeline stage live."
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "89d245d6-b908-4815-ae22-fa10e40d65dc",
"metadata": {},
"outputs": [],
"source": [
"from audio_dsp.tuning import send_config_to_device, profile_pipeline\n",
"from audio_dsp.tuning.transport import XScopeTransport\n",
"from time import sleep\n",
"\n",
"# Make a low shelf with a centre frequency of 200 Hz, q of 0.7 and gain of +6 dB\n",
"p[\"lowshelf\"].make_lowshelf(200, 0.7, 6)\n",
"p[\"lowshelf\"].plot_frequency_response()\n",
"\n",
"# Make a high shelf with a centre frequency of 4000 Hz, q of 0.7 and gain of +6 dB\n",
"p[\"highshelf\"].make_highshelf(4000, 0.7, 6)\n",
"p[\"highshelf\"].plot_frequency_response()"
]
},
{
"cell_type": "markdown",
"id": "2113ddc3",
"metadata": {},
"source": [
"3. This is the build and run cell. This stage generates an application which uses your pipeline. The tuning parameters set in the previous cell are baked in the application."
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "32492495-d82a-45ba-87e3-80b01de38981",
"metadata": {},
"outputs": [],
"source": [
"# Build and run\n",
"from audio_dsp.design.pipeline import generate_dsp_main\n",
"from audio_dsp.design.build_utils import XCommonCMakeHelper\n",
"\n",
"b = XCommonCMakeHelper()\n",
"generate_dsp_main(p)\n",
"\n",
"b.configure_build_run()"
]
}
],
"metadata": {
"kernelspec": {
"display_name": "Python 3 (ipykernel)",
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.10.13"
}
},
"nbformat": 4,
"nbformat_minor": 5
}

View File

@@ -0,0 +1,108 @@
// Copyright 2024-2025 XMOS LIMITED.
// This Software is subject to the terms of the XMOS Public Licence: Version 1.
/* System headers */
/* Standard library*/
#include <stdint.h> // for int32_t and related typedefs
#include <string.h> // for memset
/* XMOS headers */
#include <platform.h>
#include <xs1.h>
/* lib_xcore */
/* This is an incomplete list of headers exposed by lib_xcore; see the XTC
tools documentation for more details */
#include <xcore/assert.h> // for xassert()
#include <xcore/hwtimer.h> // for hwtimer_t and related functions
#include <xcore/parallel.h> // for PAR_JOBS, DECLARE_JOB, and related macros
/* Application libraries */
#include <stages/adsp_pipeline.h>
#include <adsp_generated_auto.h>
#include "whitenoise_1024samples.h"
#define SAMPLE_RATE 48000 // Hertz
#define OUTPUT_BUFFER_LENGTH 64 // samples
#define NUM_CHANNELS 1 // dimensionless
/* Set up a dummy output buffer and state variable. */
int32_t volatile output_buffer[OUTPUT_BUFFER_LENGTH];
uint8_t volatile buffer_loop_flag = 0;
uint32_t buffer_counter = 0;
DECLARE_JOB(signal_producer, (adsp_pipeline_t *));
void signal_producer(adsp_pipeline_t * m_dsp)
{
/* Get a new hardware timer and check we've got it. */
hwtimer_t event_timer = hwtimer_alloc();
xassert(event_timer);
/* Set up the first event on the hardware timer */
uint32_t const sample_period_ticks = XS1_TIMER_HZ / SAMPLE_RATE;
uint32_t trigger_time = hwtimer_get_time(event_timer) + sample_period_ticks;
hwtimer_set_trigger_time(event_timer, trigger_time);
/* Set up the input buffer. We're using precalculated white noise
* and will just loop through this over and over. */
int32_t white_noise[] = {WHITENOISE_1024};
uint32_t const n_samps = sizeof(white_noise) / sizeof(white_noise[0]);
uint32_t sample_no = 0;
while(1)
{
/* Block until the trigger time*/
hwtimer_get_time(event_timer);
/* Send the generated sample to the DSP pipeline.
* This example currently assumes one input channel. Additional members
* of this array need to be supplied in order to add more channels.
* This example also currently assumes a frame size of 1. Increasing the
* frame size would require each element of this array becoming an array
* of length FRAME_SIZE. */
int32_t * input_samples[NUM_CHANNELS] = {&white_noise[sample_no]};
sample_no = (sample_no + 1) % n_samps;
adsp_pipeline_source(m_dsp, input_samples);
/* Set up the next sample period */
trigger_time += sample_period_ticks;
hwtimer_set_trigger_time(event_timer, trigger_time);
}
}
DECLARE_JOB(signal_consumer, (adsp_pipeline_t *));
void signal_consumer(adsp_pipeline_t * m_dsp)
{
int32_t output_word = 0;
memset((void *)output_buffer, 0, sizeof(int32_t) * OUTPUT_BUFFER_LENGTH);
int32_t * output_samples[NUM_CHANNELS] = {&output_word};
while(1)
{
/* Get the processed output from the DSP pipeline.
* This operation blocks on data being available.
* We assume single-channel with frame size of 1. */
adsp_pipeline_sink(m_dsp, output_samples);
/* Place the output word in the output buffer,
* wrapping around when the buffer is full. */
output_buffer[buffer_counter] = output_word;
buffer_counter = (buffer_counter + 1) % OUTPUT_BUFFER_LENGTH;
/* Set up an easy GDB watchpoint */
if (buffer_counter == 0)
{
buffer_loop_flag = !buffer_loop_flag;
}
}
}
int main()
{
adsp_pipeline_t * m_dsp = adsp_auto_pipeline_init();
PAR_JOBS(
PJOB(signal_producer, (m_dsp)),
PJOB(signal_consumer, (m_dsp)),
PJOB(adsp_auto_pipeline_main, (m_dsp))
);
}

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,36 @@
cmake_minimum_required(VERSION 3.21)
include($ENV{XMOS_CMAKE_PATH}/xcommon.cmake)
project(app_td_block_fir)
set(APP_HW_TARGET XK-EVK-XU316)
set(APP_DEPENDENT_MODULES
"lib_audio_dsp"
"lib_logging(3.2.0)"
"lib_locks(2.2.0)"
)
set(APP_PCA_ENABLE OFF)
set(EXAMPLE_BUILD_FLAGS ${EXTRA_BUILD_FLAGS} -fcomment-asm
-Wall
-O3
-report
-lquadflash
-mcmodel=large
-g
-fxscope)
set(APP_COMPILER_FLAGS ${EXAMPLE_BUILD_FLAGS})
file(GLOB C_SRC CONFIGURE_DEPENDS RELATIVE ${CMAKE_CURRENT_LIST_DIR} src/*.c)
set(APP_C_SRCS
"${C_SRC};${DSP_MAIN}")
set(APP_INCLUDES
src
src/core
src/extensions
${CMAKE_CURRENT_LIST_DIR}/build/dsp_pipeline)
set(XMOS_SANDBOX_DIR ${CMAKE_CURRENT_LIST_DIR}/../../..)
XMOS_REGISTER_APP()

View File

@@ -0,0 +1,7 @@
app_td_block_fir
---
This demos 16 concurrent FIRs running on a single tile with a frame size of 8 and of length 4008.
Currently, it runs at 46kHz.
The application demonstrates how two channels per thread can be run in parallel over a single tile
in order to get 16 channels running concurrently.

View File

@@ -0,0 +1,17 @@
python make_test_filters.py
python ../../../python/audio_dsp/dsp/td_block_fir.py test_0.npy
python ../../../python/audio_dsp/dsp/td_block_fir.py test_1.npy
python ../../../python/audio_dsp/dsp/td_block_fir.py test_2.npy
python ../../../python/audio_dsp/dsp/td_block_fir.py test_3.npy
python ../../../python/audio_dsp/dsp/td_block_fir.py test_4.npy
python ../../../python/audio_dsp/dsp/td_block_fir.py test_5.npy
python ../../../python/audio_dsp/dsp/td_block_fir.py test_6.npy
python ../../../python/audio_dsp/dsp/td_block_fir.py test_7.npy
python ../../../python/audio_dsp/dsp/td_block_fir.py test_8.npy
python ../../../python/audio_dsp/dsp/td_block_fir.py test_9.npy
python ../../../python/audio_dsp/dsp/td_block_fir.py test_10.npy
python ../../../python/audio_dsp/dsp/td_block_fir.py test_11.npy
python ../../../python/audio_dsp/dsp/td_block_fir.py test_12.npy
python ../../../python/audio_dsp/dsp/td_block_fir.py test_13.npy
python ../../../python/audio_dsp/dsp/td_block_fir.py test_14.npy
python ../../../python/audio_dsp/dsp/td_block_fir.py test_15.npy

View File

@@ -0,0 +1,130 @@
// Copyright 2024-2025 XMOS LIMITED.
// This Software is subject to the terms of the XMOS Public Licence: Version 1.
#include <stdint.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <xcore/hwtimer.h>
#include <xcore/parallel.h>
#include <xcore/channel.h>
#include "dsp/td_block_fir.h"
#include "test_0.h"
#include "test_1.h"
#include "test_2.h"
#include "test_3.h"
#include "test_4.h"
#include "test_5.h"
#include "test_6.h"
#include "test_7.h"
#include "test_8.h"
#include "test_9.h"
#include "test_10.h"
#include "test_11.h"
#include "test_12.h"
#include "test_13.h"
#include "test_14.h"
#include "test_15.h"
#define WORKER_THREAD_COUNT 8
DECLARE_JOB(worker, (chanend_t, int32_t*, uint32_t, td_block_fir_filter_t*, int32_t*, uint32_t, td_block_fir_filter_t*));
void worker(chanend_t c,
int32_t * data0,
uint32_t data0_elements,
td_block_fir_filter_t * f0,
int32_t * data1,
uint32_t data1_elements,
td_block_fir_filter_t * f1)
{
td_block_fir_data_t d0, d1;
td_block_fir_data_init(&d0, data0, data0_elements);
td_block_fir_data_init(&d1, data1, data1_elements);
memset(data0, 0, data0_elements *sizeof(int32_t));
memset(data1, 0, data1_elements*sizeof(int32_t));
while(1){
int32_t audio_channel_0[TD_BLOCK_FIR_LENGTH];
int32_t audio_channel_1[TD_BLOCK_FIR_LENGTH];
chan_in_buf_word(c, audio_channel_0, TD_BLOCK_FIR_LENGTH);
chan_in_buf_word(c, audio_channel_1, TD_BLOCK_FIR_LENGTH);
td_block_fir_add_data(audio_channel_0, &d0);
td_block_fir_compute(audio_channel_0, &d0, f0);
td_block_fir_add_data(audio_channel_1, &d1);
td_block_fir_compute(audio_channel_1, &d1, f1);
chan_out_buf_word(c, audio_channel_0, TD_BLOCK_FIR_LENGTH);
chan_out_buf_word(c, audio_channel_1, TD_BLOCK_FIR_LENGTH);
}
}
void worker_tile(chanend_t c[WORKER_THREAD_COUNT]){
int32_t mem_0[test_0_DATA_BUFFER_ELEMENTS];
int32_t mem_1[test_1_DATA_BUFFER_ELEMENTS];
int32_t mem_2[test_2_DATA_BUFFER_ELEMENTS];
int32_t mem_3[test_3_DATA_BUFFER_ELEMENTS];
int32_t mem_4[test_4_DATA_BUFFER_ELEMENTS];
int32_t mem_5[test_5_DATA_BUFFER_ELEMENTS];
int32_t mem_6[test_6_DATA_BUFFER_ELEMENTS];
int32_t mem_7[test_7_DATA_BUFFER_ELEMENTS];
int32_t mem_8[test_8_DATA_BUFFER_ELEMENTS];
int32_t mem_9[test_9_DATA_BUFFER_ELEMENTS];
int32_t mem_10[test_10_DATA_BUFFER_ELEMENTS];
int32_t mem_11[test_11_DATA_BUFFER_ELEMENTS];
int32_t mem_12[test_12_DATA_BUFFER_ELEMENTS];
int32_t mem_13[test_13_DATA_BUFFER_ELEMENTS];
int32_t mem_14[test_14_DATA_BUFFER_ELEMENTS];
int32_t mem_15[test_15_DATA_BUFFER_ELEMENTS];
PAR_JOBS (
PJOB(worker, (c[0], mem_0, test_0_DATA_BUFFER_ELEMENTS, &td_block_fir_filter_test_0,
mem_1, test_1_DATA_BUFFER_ELEMENTS, &td_block_fir_filter_test_1)),
PJOB(worker, (c[1], mem_2, test_2_DATA_BUFFER_ELEMENTS, &td_block_fir_filter_test_2,
mem_3, test_3_DATA_BUFFER_ELEMENTS, &td_block_fir_filter_test_3)),
PJOB(worker, (c[2], mem_4, test_4_DATA_BUFFER_ELEMENTS, &td_block_fir_filter_test_4,
mem_5, test_5_DATA_BUFFER_ELEMENTS, &td_block_fir_filter_test_5)),
PJOB(worker, (c[3], mem_6, test_6_DATA_BUFFER_ELEMENTS, &td_block_fir_filter_test_6,
mem_7, test_7_DATA_BUFFER_ELEMENTS, &td_block_fir_filter_test_7)),
PJOB(worker, (c[4], mem_8, test_8_DATA_BUFFER_ELEMENTS, &td_block_fir_filter_test_8,
mem_9, test_9_DATA_BUFFER_ELEMENTS, &td_block_fir_filter_test_9)),
PJOB(worker, (c[5], mem_10, test_10_DATA_BUFFER_ELEMENTS, &td_block_fir_filter_test_10,
mem_11, test_11_DATA_BUFFER_ELEMENTS, &td_block_fir_filter_test_11)),
PJOB(worker, (c[6], mem_12, test_12_DATA_BUFFER_ELEMENTS, &td_block_fir_filter_test_12,
mem_13, test_13_DATA_BUFFER_ELEMENTS, &td_block_fir_filter_test_13)),
PJOB(worker, (c[7], mem_14, test_14_DATA_BUFFER_ELEMENTS, &td_block_fir_filter_test_14,
mem_15, test_15_DATA_BUFFER_ELEMENTS, &td_block_fir_filter_test_15))
);
}
void audio_gen(chanend_t c[WORKER_THREAD_COUNT]){
hwtimer_t SysTimer = hwtimer_alloc();
uint32_t from, to;
const uint32_t loops = 128;
from = hwtimer_get_time(SysTimer);
int32_t buffer0[TD_BLOCK_FIR_LENGTH];
int32_t buffer1[TD_BLOCK_FIR_LENGTH];
for(int i=0;i<loops;i++){
//send the unfiltered samples
for(int worker_idx=0;worker_idx < WORKER_THREAD_COUNT;worker_idx++){
chan_out_buf_word(c[worker_idx], buffer0, TD_BLOCK_FIR_LENGTH);
chan_out_buf_word(c[worker_idx], buffer1, TD_BLOCK_FIR_LENGTH);
}
//recieve the filtered samples
for(int worker_idx=0;worker_idx < WORKER_THREAD_COUNT;worker_idx++){
chan_in_buf_word(c[worker_idx], buffer0, TD_BLOCK_FIR_LENGTH);
chan_in_buf_word(c[worker_idx], buffer1, TD_BLOCK_FIR_LENGTH);
}
}
to = hwtimer_get_time(SysTimer);
uint32_t elapsed = to - from;
printf("elapsed: %lu\n", elapsed/loops);
exit(1);
}

View File

@@ -0,0 +1,13 @@
# Copyright 2024-2025 XMOS LIMITED.
# This Software is subject to the terms of the XMOS Public Licence: Version 1.
import numpy as np
from scipy import signal
def bandpass_filters(length, count):
for i in range(count):
t = signal.firwin(length, (i + 1) / (count + 2))
name = 'test_' + str(i)
np.save(name, t)
if __name__ == '__main__':
bandpass_filters(4008, 16)

View File

@@ -0,0 +1,15 @@
// Copyright 2024-2025 XMOS LIMITED.
// This Software is subject to the terms of the XMOS Public Licence: Version 1.
#include <platform.h>
void audio_gen(chanend c[8]);
void worker_tile(chanend c[8]);
int main(){
chan c[8];
par {
on tile[0] : audio_gen(c);
on tile[1] : worker_tile(c);
}
return 0;
}