Files
3d_audio/lib_src/lib_src/api/asynchronous_fifo.h
Steven Dan b43581c2ff add libs
2026-05-12 11:17:20 +08:00

225 lines
9.6 KiB
C

// Copyright 2023-2024 XMOS LIMITED.
// This Software is subject to the terms of the XMOS Public Licence: Version 1.
#ifndef _asynchronous_fifo_h__
#define _asynchronous_fifo_h__
#include <stdint.h>
#include <xccompat.h>
#define FREQUENCY_RATIO_EXPONENT (32)
#ifdef __XC__
#define UNSAFE unsafe
#else
#define UNSAFE
#endif
/**
* \addtogroup src_fifo src_fifo
*
* The public API for using Asynchronous FIFO.
* @{
*/
/**
* Data structure that holds the status of an asynchronous FIFO
*/
typedef struct asynchronous_fifo_t {
// Updated on initialisation only
int32_t channel_count; /* Number of audio channels */
int32_t copy_mask; /* Number of audio channels */
int32_t max_fifo_depth; /* Length of buffer[] in channel_counts */
int32_t ideal_phase_error_ticks; /* Ideal ticks between samples */
int32_t Ki; /* Ki PID coefficient */
int32_t Kp; /* Kp PID coefficient */
// Updated on the producer side only
int skip_ctr; /* Set to indicate initialisation runs */
int32_t write_ptr; /* Write index in the buffer */
int64_t last_phase_error; /* previous error, used for proportional */
int64_t frequency_ratio; /* Current ratio of frequencies in 64.64 */
int32_t stop_producing; /* In case of overflow, stops producer until consumer restarts and requests a reset */
// Updated on the consumer side only
uint32_t read_ptr; /* Read index in the buffer */
// Set by producer, reset by consumer
uint32_t reset; /* Set to 1 if consumer wants a reset */
// Updated from both sides
uint32_t * UNSAFE timestamps; /* Timestamps of samples */
int32_t buffer[0]; /* Buffer of data */
} asynchronous_fifo_t;
/**
* Function that must be called to initialise the asynchronous FIFO.
* 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.
*
* After initialising, you must initialise the PID by calling one of
* asynchronous_fifo_init_PID_fs_codes() or
* asynchronous_fifo_init_PID_raw()
*
* @param state Asynchronous FIFO to be initialised
*
* @param channel_count Number of audio channels
*
* @param max_fifo_depth Length of the FIFO, delay when stable will be max_fifo_depth/2
*/
void asynchronous_fifo_init(asynchronous_fifo_t * UNSAFE state,
int channel_count,
int max_fifo_depth);
/**
* Function that that initialises the PID of a FIFO. Either this function
* or asynchronous_fifo_init_PID_raw() should be called. This function
* uses frequency codes as defined in the ASRC for a quick default setup,
* the raw function allows full control
*
* @param state Asynchronous FIFO to be initialised
*
* @param fs_input Input FS ratio, used to pick appropriate Kp, and Ki.
* Must be a number less than 6.
*
* @param fs_output Input FS ratio, used to pick appropriate Kp, Ki,
* ideal phase. Must be a number less than 6.
*/
void asynchronous_fifo_init_PID_fs_codes(asynchronous_fifo_t * UNSAFE state,
int fs_input,
int fs_output);
/**
* Function that that initialises the PID of a FIFO. Either this function
* or asynchronous_fifo_init_PID_raw() should be called. This function
* uses frequency codes as defined in the ASRC for a quick default setup,
* the raw function allows full control.
*
* This function may be called at any time by the producer in order to alter the PID
* and midpoint settings. It does not reset the error; one of the
* asynchronous_fifo_init_reset_producer() or
* asynchronous_fifo_init_reset_consumer() functions should be called for that.
*
* @param state Asynchronous FIFO to be initialised
*
* @param Kp Proportional constant for the FIFO.
* This gets multiplied by the differential
* error measured in ticks (typically -2..2)
* and added to the ratio_error. A typical
* value is 30,000,000 - 60,000,000.
*
* @param Ki Integral constant for the FIFO.
* This gets multiplied by the phase error
* measured in ticks (typically -20,000 - 20,000)
* and added to the ratio_error.
* A typical value is 200 - 300.
*
* @param ticks_between_samples The number of ticks between samples is used to
* estimate the expected phase error halfway down
* the FIFO.
*/
void asynchronous_fifo_init_PID_raw(asynchronous_fifo_t * UNSAFE state,
int Kp,
int Ki,
int ticks_between_samples);
/**
* Function that that resets the FIFO from the producer side. Either this function should
* be called on the producing side, or ``asynchronous_fifo_reset_consumer``
* should be called on the consumer side. In both cases the whole FIFO will be reset back
*
* @param state Asynchronous FIFO to be initialised
*/
void asynchronous_fifo_reset_producer(asynchronous_fifo_t * UNSAFE state);
/**
* Function that that resets the FIFO from the consumer side. Either this function should
* be called on the consuming side, or asynchronous_fifo_reset_producer()
* should be called on the producer side. In both cases the whole FIFO will be reset back
*
* @param state Asynchronous FIFO to be initialised
*/
void asynchronous_fifo_reset_consumer(asynchronous_fifo_t * UNSAFE state);
/**
* Function that must be called to deinitalise the asynchronous FIFO
*
* @param state ASRC structure to be de-initialised
*/
void asynchronous_fifo_exit(asynchronous_fifo_t * UNSAFE state);
/**
* Function that provides the next samples to the asynchronous FIFO.
*
* This function and asynchronous_fifo_consumer_get() function both need a timestamp,
* which is the time that the last sample was input (this function) or
* output (asynchronous_fifo_consumer_get()). The asynchronous FIFO will hand the
* samples across from producer to consumer through an elastic queue, and run
* a PID algorithm to calculate the best way to equalise the input clock relative
* to the output clock. Therefore, the timestamps
* have to be measured on either the same clock or two very similar clocks.
* It is probably fine to use the reference clocks on two tiles, provided
* the tiles came out of reset at more or less the same time. Using the
* clocks from two different chips would require the two chips to share an
* oscillator, and for them to come out of reset simultaneously.
*
* @param state ASRC structure to push the sample into
*
* @param samples The sample values.
*
* @param n The number of samples
*
* @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,
* a value less than zero means that the producer is faster than the consumer,
* a value greater than zero means that the producer is slower than the consumer.
* The value should be scaled by 2**-32. That is, the current best
* approximation for consumer_speed/producer_speed is 1 + (return_value * 2**-32)
*
* The output is filtered and should be applied directly as a correction factor
* eg, multiplied into an ASRC ratio, or multiplied into a PLL timing.
*/
int32_t asynchronous_fifo_producer_put(asynchronous_fifo_t * UNSAFE state,
int32_t * UNSAFE samples,
int n,
int32_t timestamp,
int xscope_used);
/**
* Function that gets an output sample from the asynchronous FIFO
*
* @param state ASRC structure to read a sample out off.
*
* @param samples The array where the frame with output
* samples will be stored.
*
* @param timestamp A timestamp taken at the time that the
* last sample was output. See
* ``asynchronous_fifo_produce`` for requirements.
*/
void asynchronous_fifo_consumer_get(asynchronous_fifo_t * UNSAFE state,
int32_t * UNSAFE samples,
int32_t timestamp);
/**
* macro that calculates the number of int64_t to be allocated for the fifo
* for a FIFO of N elements and C channels
*/
#define ASYNCHRONOUS_FIFO_INT64_ELEMENTS(N, C) (sizeof(asynchronous_fifo_t)/sizeof(int64_t) + (N*(C+1))/2+1)
#endif
/**@}*/ // END: addtogroup src_fifo