Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions CHANGELOG.rst
Original file line number Diff line number Diff line change
@@ -1,6 +1,13 @@
lib_src change log
==================

3.0.0
-----

* FIXED: Double buffer asrc_io.input_timestamp to prevent producer timestamp
getting overwritten during asrc processing
* REMOVED: xscope_used argument from the asynchronous_fifo_producer_put() API

2.5.0
-----

Expand Down
6 changes: 3 additions & 3 deletions doc/asrc_task/asrc_task.rst
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ The ASRC Task supports the following nominal sample rates for input and output:
- 88.2 kHz
- 96 kHz
- 176.4 kHz
- 192 kHz
- 192 kHz

Because the required compute for multi-channel systems may exceed the performance limit of a single thread, the ASRC subsystem is able to make use of multiple threads in parallel to achieve the required conversion within the sample time period. It uses a dynamic fork and join architecture to share the ASRC workload across multiple threads each time a batch of samples is processed. The threads must all reside on the same tile as the ASRC task due to them sharing input and output buffers. The workload and buffer partitioning is dynamically computed by the ASRC task at stream startup and is constrained by the user at compile time to set maximum limits of both channel count and worker threads.

Expand All @@ -40,7 +40,7 @@ The difference between the performance requirement between the two architectures

- An eight channel system consisting of either 44.1kHz or 48kHz input with maximum output rate of 192kHz will require about (0.15 * (48 + 192) * 8) ~= 288 thread MHz. This can adequately be provided by four threads (assuming up to 8 active threads on an xcore.ai device with a 600MHz clock).

In reality the amount of thread MHz needed will be lower than the above formulae suggest since subsequent ASRC channels after the first can share some of the calculations. This results in about at 10% performance requirement reduction per additional channel per worker thread. Increasing the input frame size in the ASRC task may also reduce the MHz requirement a few % at the cost of larger buffers and a slight latency increase.
In reality the amount of thread MHz needed will be lower than the above formulae suggest since subsequent ASRC channels after the first can share some of the calculations. This results in about at 10% performance requirement reduction per additional channel per worker thread. Increasing the input frame size in the ASRC task may also reduce the MHz requirement a few % at the cost of larger buffers and a slight latency increase.

.. warning::
Exceeding the processing time available by specifying a channel count, input/output rates, number of worker threads or device clock speed may result in at best choppy audio or a blocked ASRC task if the overrun is persistent.
Expand Down Expand Up @@ -102,7 +102,7 @@ An example of the user-defined `C` function for receiving the input samples is s

// Receive stream info from producer
*new_input_rate = chanend_in_word(c_producer);
asrc_io->input_timestamp = chanend_in_word(c_producer);
asrc_io->input_timestamp[asrc_io->input_write_idx] = chanend_in_word(c_producer);
asrc_io->input_channel_count = chanend_in_word(c_producer);

// Pack into array properly LRLRLRLR or 123412341234 etc.
Expand Down
7 changes: 2 additions & 5 deletions lib_src/api/asynchronous_fifo.h
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ typedef struct asynchronous_fifo_t_ {
* The ``state`` argument should be an int64_t array of
* ``ASYNCHRONOUS_FIFO_INT64_ELEMENTS`` elements that is cast to
* ``asynchronous_fifo_t*``.
*
*
* That pointer should also be used for all other operations, including operations
* both the consumer and producer sides.
*
Expand Down Expand Up @@ -177,8 +177,6 @@ void asynchronous_fifo_exit(asynchronous_fifo_t * UNSAFE state);
*
* @param timestamp The number of ticks when this sample was input.
*
* @param xscope_used Set to 1 if the PID values should be output over
* xscope. Used for debugging. This parameter is subject to be removed in future revisions.
*
* @returns The current estimate of the mismatch of input and output frequencies.
* This is represented as a 32-bit signed number. Zero means no mismatch,
Expand All @@ -193,8 +191,7 @@ void asynchronous_fifo_exit(asynchronous_fifo_t * UNSAFE state);
int32_t asynchronous_fifo_producer_put(asynchronous_fifo_t * UNSAFE state,
int32_t * UNSAFE samples,
int n,
int32_t timestamp,
int xscope_used);
int32_t timestamp);


/**
Expand Down
2 changes: 1 addition & 1 deletion lib_src/lib_build_info.cmake
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
set(LIB_NAME lib_src)
set(LIB_VERSION 2.5.0)
set(LIB_VERSION 3.0.0)

set(LIB_DEPENDENT_MODULES "lib_logging(3.2.0)")

Expand Down
2 changes: 1 addition & 1 deletion lib_src/module_build_info
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
VERSION = 2.5.0
VERSION = 3.0.0

DEPENDENT_MODULES = lib_logging(>=3.2.0)

Expand Down
26 changes: 12 additions & 14 deletions lib_src/src/asrc_task/asrc_task.c
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ void do_asrc_group(schedule_info_t *schedule, uint64_t fs_ratio, asrc_in_out_t *
// Pack into the frame this instance of ASRC expects
int input_samples[ASRC_N_IN_SAMPLES * MAX_ASRC_CHANNELS_TOTAL];
for(int i = 0; i < ASRC_N_IN_SAMPLES * num_worker_channels; i++){
// int rd_idx = i % num_worker_channels + (i / num_worker_channels) * asrc_io->asrc_channel_count + worker_channel_start_idx;
// int rd_idx = i % num_worker_channels + (i / num_worker_channels) * asrc_io->asrc_channel_count + worker_channel_start_idx;
int rd_idx = i + (asrc_io->asrc_channel_count - num_worker_channels) * (i / num_worker_channels) + worker_channel_start_idx; // Optimisation of above
input_samples[i] = asrc_io->input_samples[input_write_idx][rd_idx];
}
Expand Down Expand Up @@ -193,13 +193,13 @@ void reset_asrc_fifo_consumer(asynchronous_fifo_t * fifo){
}

// Default implementation of receive (called from ASRC) which receives samples and config over a channel. This is overridable.
ASRC_TASK_ISR_CALLBACK_ATTR
ASRC_TASK_ISR_CALLBACK_ATTR
unsigned receive_asrc_input_samples_cb_default(chanend_t c_asrc_input, asrc_in_out_t *asrc_io, unsigned *new_input_rate){
static unsigned asrc_in_counter = 0;

// Get format and timing data from channel
*new_input_rate = chanend_in_word(c_asrc_input);
asrc_io->input_timestamp = chanend_in_word(c_asrc_input);
asrc_io->input_timestamp[asrc_io->input_write_idx] = chanend_in_word(c_asrc_input);
asrc_io->input_channel_count = chanend_in_word(c_asrc_input);

// Pack into array properly LRLRLRLR for 2ch or 123412341234 for 4ch etc.
Expand Down Expand Up @@ -232,7 +232,7 @@ DEFINE_INTERRUPT_CALLBACK(ASRC_ISR_GRP, asrc_samples_rx_isr_handler, app_data){
chanend_t c_buff_idx = asrc_receive_samples_ctx->c_buff_idx;
asrc_in_out_t *asrc_io = asrc_receive_samples_ctx->asrc_io;
ASRC_TASK_ISR_CALLBACK_ATTR asrc_task_produce_isr_cb_t receive_asrc_input_samples_cb = asrc_io->asrc_task_produce_cb;

// Always consume samples so we don't apply backpressure to the producer
// Call the user defined receive samples callback.
ASRC_TASK_ISR_CALLBACK_ATTR
Expand All @@ -242,7 +242,7 @@ DEFINE_INTERRUPT_CALLBACK(ASRC_ISR_GRP, asrc_samples_rx_isr_handler, app_data){
if(asrc_in_counter == 0 && asrc_io->ready_flag_to_receive){
// Note if you ever find the code has stopped here then this is due to the time required to ASRC process the input frame
// is longer than the period of the frames coming in. To remedy this you need to increase ASRC processing resources or reduce
// the processing requirement. If you are using XCORE-200, consider using xcore.ai for more than 2x the ASRC performance.
// the processing requirement. If you are using XCORE-200, consider using xcore.ai for more than 2x the ASRC performance.
// Notify ASRC main loop of new frame
chanend_out_byte(c_buff_idx, (uint8_t)asrc_io->input_write_idx);
asrc_io->input_write_idx ^= 1; // Swap buffers
Expand Down Expand Up @@ -274,7 +274,7 @@ static inline void asrc_wait_for_valid_config(chanend_t c_buff_idx, uint32_t *in

// Check to see if input params have changed since last process
static inline bool asrc_detect_format_change(uint32_t input_frequency, uint32_t output_frequency, asrc_in_out_t *asrc_io){
if( asrc_io->input_frequency != input_frequency ||
if( asrc_io->input_frequency != input_frequency ||
asrc_io->input_channel_count != asrc_io->asrc_channel_count ||
asrc_io->output_frequency != output_frequency){

Expand All @@ -291,7 +291,7 @@ DEFINE_INTERRUPT_PERMITTED(ASRC_ISR_GRP, void, asrc_processor,
asrc_in_out_t *asrc_io,
chanend_t c_buff_idx,
asynchronous_fifo_t * fifo){

uint32_t input_frequency = 0; // Set to invalid for now. We will get rates supplied by producer and consumer.
uint32_t output_frequency = 0;

Expand All @@ -304,7 +304,7 @@ DEFINE_INTERRUPT_PERMITTED(ASRC_ISR_GRP, void, asrc_processor,
{ 2268, 2268, 1134, 1134, 567, 567},
{ 2083, 2083, 1042, 1042, 521, 521}
};

// Setup a pointer to a struct so the ISR can access these elements
asrc_receive_samples_ctx_t asrc_receive_samples_ctx = {c_asrc_input, c_buff_idx, asrc_io};

Expand All @@ -322,7 +322,7 @@ DEFINE_INTERRUPT_PERMITTED(ASRC_ISR_GRP, void, asrc_processor,
int inputFsCode = frequency_to_fs_code(input_frequency);
int outputFsCode = frequency_to_fs_code(output_frequency);
int interpolation_ticks = interpolation_ticks_2D[inputFsCode][outputFsCode];

//// FIFO init
dprintf("FIFO init channels: %d length: %ld\n", asrc_io->asrc_channel_count, fifo->max_fifo_depth);
asynchronous_fifo_init(fifo, asrc_io->asrc_channel_count, fifo->max_fifo_depth);
Expand Down Expand Up @@ -367,8 +367,6 @@ DEFINE_INTERRUPT_PERMITTED(ASRC_ISR_GRP, void, asrc_processor,
ideal_fs_ratio = (fs_ratio + (1<<31)) >> 32;
dprintf("ideal_fs_ratio: %d\n", ideal_fs_ratio);

const int xscope_used = 0; // Vestige of ASRC API. TODO - cleanup in future when lib_src is tidied

asrc_io->ready_flag_to_receive = 1; // Signal we are ready to consume a frame of input samples
asrc_io->ready_flag_configured = 1; // SIgnal we are ready to produce

Expand All @@ -385,10 +383,10 @@ DEFINE_INTERRUPT_PERMITTED(ASRC_ISR_GRP, void, asrc_processor,

int32_t t0 = get_reference_time();
int num_output_samples = par_asrc(num_jobs, schedule, fs_ratio, asrc_io, input_write_idx, sASRCCtrl);
int ts = asrc_timestamp_interpolation(asrc_io->input_timestamp, sASRCCtrl[0], interpolation_ticks);
int ts = asrc_timestamp_interpolation(asrc_io->input_timestamp[input_write_idx], sASRCCtrl[0], interpolation_ticks);
// Only push to FIFO if we have samples (FIFO has a bug) otherwise hold last error value
if(num_output_samples){
error = asynchronous_fifo_producer_put(fifo, &asrc_io->output_samples[0], num_output_samples, ts, xscope_used);
error = asynchronous_fifo_producer_put(fifo, &asrc_io->output_samples[0], num_output_samples, ts);
}

fs_ratio = (((int64_t)ideal_fs_ratio) << 32) + (error * (int64_t) ideal_fs_ratio);
Expand All @@ -409,7 +407,7 @@ DEFINE_INTERRUPT_PERMITTED(ASRC_ISR_GRP, void, asrc_processor,
}


// Wrapper to setup ISR->task signalling chanend and use ISR friendly call to function
// Wrapper to setup ISR->task signalling chanend and use ISR friendly call to function
void asrc_task(chanend_t c_asrc_input, asrc_in_out_t *asrc_io, asynchronous_fifo_t *fifo, unsigned fifo_length){
// Check callback is init'd. If not, use default implementation.
if (asrc_io->asrc_task_produce_cb == NULL){
Expand Down
20 changes: 10 additions & 10 deletions lib_src/src/asrc_task/asrc_task.h
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@
#define SRC_DITHER_SETTING 0

#else
// Check for required static defines
// Check for required static defines
#include "asrc_task_config.h"

#ifndef MAX_ASRC_CHANNELS_TOTAL
Expand All @@ -53,7 +53,7 @@
#endif

/** @brief Decorator for user ASRC producer receive callback. Must be used to allow stack usage calculation. */
#define ASRC_TASK_ISR_CALLBACK_ATTR __attribute__((fptrgroup("asrc_callback_isr_fptr_grp")))
#define ASRC_TASK_ISR_CALLBACK_ATTR __attribute__((fptrgroup("asrc_callback_isr_fptr_grp")))

#ifndef __DOXYGEN__

Expand Down Expand Up @@ -90,11 +90,11 @@ typedef struct asrc_in_out_t_{
int32_t input_samples[2][ASRC_N_IN_SAMPLES * MAX_ASRC_CHANNELS_TOTAL];
/** Double buffer idx */
unsigned input_write_idx;
/** Timestamp of last received input sample */
int32_t input_timestamp;
/** Nominal input sample rate 44100..192000 (set by producer) */
/** Timestamp of last received input sample. Double buffered */
int32_t input_timestamp[2];
/** Nominal input sample rate 44100..192000 (set by producer) */
unsigned input_frequency;
/** This is set by the producer and can change dynamically */
/** This is set by the producer and can change dynamically */
unsigned input_channel_count;
/** The function pointer of the ASRC_TASK producer receive callback. Must be defined by user to receive samples from producer over channel. */
void * UNSAFE asrc_task_produce_cb;
Expand All @@ -111,7 +111,7 @@ typedef struct asrc_in_out_t_{
unsigned asrc_channel_count;
/** Flag to indicate ASRC ready to accept samples */
int ready_flag_to_receive;
/** Flag to indicate ASRC is configured and OK to pull from FIFO */
/** Flag to indicate ASRC is configured and OK to pull from FIFO */
int ready_flag_configured;

}asrc_in_out_t;
Expand Down Expand Up @@ -161,9 +161,9 @@ void reset_asrc_fifo_consumer(asynchronous_fifo_t * UNSAFE fifo);
* Prototype that can optionally be defined by the user to initialise the function pointer for the ASRC receive produced samples ISR.
* If this is not called then receive_asrc_input_samples_cb_default() is used and the you may call send_asrc_input_samples_default()
* from the application to send samples to the ASRC task.
*
*
* Must be called before running asrc_task()
*
*
* \param asrc_io A pointer to the structure used for holding ASRC IO and state.
* \param asrc_rx_fp A pointer to the user asrc_receive_samples function. NOTE - This MUST be decorated by ASRC_TASK_ISR_CALLBACK_ATTR
* to allow proper stack calculation by the compiler. See receive_asrc_input_samples_cb_default() in asrc_task.c
Expand All @@ -176,7 +176,7 @@ void init_asrc_io_callback(asrc_in_out_t * UNSAFE asrc_io, asrc_task_produce_isr
/**
* If the init_asrc_io_callback() function is not called then a default implementation of the ASRC receive will be used.
* This send function (called by the user producer side) mirrors the receive and can be used to push samples into the ASRC.
*
*
* \param c_asrc_input The chan end on the application producer side connecting to the ASRC task.
* \param input_frequency The sample rate of the input stream (44100, 48000, ...).
* \param input_timestamp The ref clock timestamp of latest received input sample.
Expand Down
11 changes: 3 additions & 8 deletions lib_src/src/asynchronous_fifo.c
Original file line number Diff line number Diff line change
Expand Up @@ -110,8 +110,7 @@ void asynchronous_fifo_reset_consumer(asynchronous_fifo_t *state) {

int32_t asynchronous_fifo_producer_put(asynchronous_fifo_t *state, int32_t *samples,
int n,
int32_t timestamp,
int xscope_used) {
int32_t timestamp) {
int read_ptr = state->read_ptr;
int write_ptr = state->write_ptr;
int max_fifo_depth = state->max_fifo_depth;
Expand Down Expand Up @@ -160,21 +159,17 @@ int32_t asynchronous_fifo_producer_put(asynchronous_fifo_t *state, int32_t *samp
state->frequency_ratio +=
(diff_error * (int64_t) (state->Kp / n)) + // TODO: make this lookup table
(phase_error * (int64_t) state->Ki);
if (xscope_used) {
#if defined(ASYNC_FIFO_XSCOPE_INSTRUMENTATION)
#if defined(ASYNC_FIFO_XSCOPE_INSTRUMENTATION)
xscope_int(1, phase_error);
xscope_int(2, diff_error);
#endif
}
}
state->last_phase_error = phase_error;
}
if (xscope_used) {
#if defined(ASYNC_FIFO_XSCOPE_INSTRUMENTATION)
#if defined(ASYNC_FIFO_XSCOPE_INSTRUMENTATION)
xscope_int(3, len);
xscope_int(4, state->frequency_ratio >> K_SHIFT);
#endif
}
return (state->frequency_ratio + (1<<(K_SHIFT-1))) >> K_SHIFT;
}

Expand Down
2 changes: 1 addition & 1 deletion settings.yml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
---
project: lib_src
title: SAMPLE RATE CONVERSION
version: 2.5.0
version: 3.0.0

documentation:
exclude_patterns_path: doc/exclude_patterns.inc
Expand Down
4 changes: 2 additions & 2 deletions tests/asrc_task_test/asrc_task_receive_samples.c
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ unsigned receive_asrc_input_samples(chanend_t c_producer, asrc_in_out_t *asrc_io

// Receive stream info from producer
*new_input_rate = chanend_in_word(c_producer);
asrc_io->input_timestamp = chanend_in_word(c_producer);
asrc_io->input_timestamp[asrc_io->input_write_idx] = chanend_in_word(c_producer);
asrc_io->input_channel_count = chanend_in_word(c_producer);

// Pack into array properly LRLRLRLR or 123412341234 etc.
Expand All @@ -29,4 +29,4 @@ unsigned receive_asrc_input_samples(chanend_t c_producer, asrc_in_out_t *asrc_io
// Register the above function for ASRC task
void setup_asrc_io_custom_callback(asrc_in_out_t *asrc_io){
init_asrc_io_callback(asrc_io, receive_asrc_input_samples);
}
}
2 changes: 1 addition & 1 deletion tests/asynchronous_fifo_asrc_test/Makefile
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
TARGET = XCORE-AI-EXPLORER
APP_NAME =
USED_MODULES = lib_src
XCC_FLAGS = -O3 -fxscope -DASYNC_FIFO_XSCOPE_INSTRUMENTATION
XCC_FLAGS = -O3 -fxscope

include $(XMOS_MAKE_PATH)/xcommon/module_xcommon/build/Makefile.common

Loading