Merge pull request #804 from hathach/audio_make_dma_ready

Implement functions to allow for DMA usage in audio driver.
This commit is contained in:
Ha Thach
2021-05-02 15:29:38 +07:00
committed by GitHub
11 changed files with 372 additions and 215 deletions

View File

@@ -57,7 +57,8 @@ static inline void _ff_unlock(tu_fifo_mutex_t mutex)
#endif
/** \enum tu_fifo_copy_mode_t
* \brief Write modes intended to allow special read and write functions to be able to copy data to and from USB hardware FIFOs as needed for e.g. STM32s and others
* \brief Write modes intended to allow special read and write functions to be able to
* copy data to and from USB hardware FIFOs as needed for e.g. STM32s and others
*/
typedef enum
{
@@ -77,7 +78,10 @@ bool tu_fifo_config(tu_fifo_t *f, void* buffer, uint16_t depth, uint16_t item_si
f->item_size = item_size;
f->overwritable = overwritable;
f->max_pointer_idx = 2*depth - 1; // Limit index space to 2*depth - this allows for a fast "modulo" calculation but limits the maximum depth to 2^16/2 = 2^15 and buffer overflows are detectable only if overflow happens once (important for unsupervised DMA applications)
// Limit index space to 2*depth - this allows for a fast "modulo" calculation
// but limits the maximum depth to 2^16/2 = 2^15 and buffer overflows are detectable
// only if overflow happens once (important for unsupervised DMA applications)
f->max_pointer_idx = 2*depth - 1;
f->non_used_index_space = UINT16_MAX - f->max_pointer_idx;
f->rd_idx = f->wr_idx = 0;
@@ -319,7 +323,8 @@ static void _ff_pull_n(tu_fifo_t* f, void* app_buf, uint16_t n, uint16_t rel, tu
static uint16_t advance_pointer(tu_fifo_t* f, uint16_t p, uint16_t offset)
{
// We limit the index space of p such that a correct wrap around happens
// Check for a wrap around or if we are in unused index space - This has to be checked first!! We are exploiting the wrap around to the correct index
// Check for a wrap around or if we are in unused index space - This has to be checked first!!
// We are exploiting the wrap around to the correct index
if ((p > p + offset) || (p + offset > f->max_pointer_idx))
{
p = (p + offset) + f->non_used_index_space;
@@ -335,7 +340,8 @@ static uint16_t advance_pointer(tu_fifo_t* f, uint16_t p, uint16_t offset)
static uint16_t backward_pointer(tu_fifo_t* f, uint16_t p, uint16_t offset)
{
// We limit the index space of p such that a correct wrap around happens
// Check for a wrap around or if we are in unused index space - This has to be checked first!! We are exploiting the wrap around to the correct index
// Check for a wrap around or if we are in unused index space - This has to be checked first!!
// We are exploiting the wrap around to the correct index
if ((p < p - offset) || (p - offset > f->max_pointer_idx))
{
p = (p - offset) - f->non_used_index_space;
@@ -348,9 +354,9 @@ static uint16_t backward_pointer(tu_fifo_t* f, uint16_t p, uint16_t offset)
}
// get relative from absolute pointer
static uint16_t get_relative_pointer(tu_fifo_t* f, uint16_t p, uint16_t offset)
static uint16_t get_relative_pointer(tu_fifo_t* f, uint16_t p)
{
return _ff_mod(advance_pointer(f, p, offset), f->depth);
return _ff_mod(p, f->depth);
}
// Works on local copies of w and r - return only the difference and as such can be used to determine an overflow
@@ -396,7 +402,7 @@ static inline void _tu_fifo_correct_read_pointer(tu_fifo_t* f, uint16_t wAbs)
// Works on local copies of w and r
// Must be protected by mutexes since in case of an overflow read pointer gets modified
static bool _tu_fifo_peek_at(tu_fifo_t* f, uint16_t offset, void * p_buffer, uint16_t wAbs, uint16_t rAbs)
static bool _tu_fifo_peek(tu_fifo_t* f, void * p_buffer, uint16_t wAbs, uint16_t rAbs)
{
uint16_t cnt = _tu_fifo_count(f, wAbs, rAbs);
@@ -408,9 +414,9 @@ static bool _tu_fifo_peek_at(tu_fifo_t* f, uint16_t offset, void * p_buffer, uin
}
// Skip beginning of buffer
if (cnt == 0 || offset >= cnt) return false;
if (cnt == 0) return false;
uint16_t rRel = get_relative_pointer(f, rAbs, offset);
uint16_t rRel = get_relative_pointer(f, rAbs);
// Peek data
_ff_pull(f, p_buffer, rRel);
@@ -420,7 +426,7 @@ static bool _tu_fifo_peek_at(tu_fifo_t* f, uint16_t offset, void * p_buffer, uin
// Works on local copies of w and r
// Must be protected by mutexes since in case of an overflow read pointer gets modified
static uint16_t _tu_fifo_peek_at_n(tu_fifo_t* f, uint16_t offset, void * p_buffer, uint16_t n, uint16_t wAbs, uint16_t rAbs, tu_fifo_copy_mode_t copy_mode)
static uint16_t _tu_fifo_peek_n(tu_fifo_t* f, void * p_buffer, uint16_t n, uint16_t wAbs, uint16_t rAbs, tu_fifo_copy_mode_t copy_mode)
{
uint16_t cnt = _tu_fifo_count(f, wAbs, rAbs);
@@ -433,13 +439,12 @@ static uint16_t _tu_fifo_peek_at_n(tu_fifo_t* f, uint16_t offset, void * p_buffe
}
// Skip beginning of buffer
if (cnt == 0 || offset >= cnt) return 0;
if (cnt == 0) return 0;
// Check if we can read something at and after offset - if too less is available we read what remains
cnt -= offset;
if (cnt < n) n = cnt;
uint16_t rRel = get_relative_pointer(f, rAbs, offset);
uint16_t rRel = get_relative_pointer(f, rAbs);
// Peek data
_ff_pull_n(f, p_buffer, n, rRel, copy_mode);
@@ -479,7 +484,7 @@ static uint16_t _tu_fifo_write_n(tu_fifo_t* f, const void * data, uint16_t n, tu
w = r;
}
uint16_t wRel = get_relative_pointer(f, w, 0);
uint16_t wRel = get_relative_pointer(f, w);
// Write data
_ff_push_n(f, buf8, n, wRel, copy_mode);
@@ -497,7 +502,8 @@ static uint16_t _tu_fifo_read_n(tu_fifo_t* f, void * buffer, uint16_t n, tu_fifo
_ff_lock(f->mutex_rd);
// Peek the data
n = _tu_fifo_peek_at_n(f, 0, buffer, n, f->wr_idx, f->rd_idx, copy_mode); // f->rd_idx might get modified in case of an overflow so we can not use a local variable
// f->rd_idx might get modified in case of an overflow so we can not use a local variable
n = _tu_fifo_peek_n(f, buffer, n, f->wr_idx, f->rd_idx, copy_mode);
// Advance read pointer
f->rd_idx = advance_pointer(f, f->rd_idx, n);
@@ -635,7 +641,8 @@ bool tu_fifo_read(tu_fifo_t* f, void * buffer)
_ff_lock(f->mutex_rd);
// Peek the data
bool ret = _tu_fifo_peek_at(f, 0, buffer, f->wr_idx, f->rd_idx); // f->rd_idx might get modified in case of an overflow so we can not use a local variable
// f->rd_idx might get modified in case of an overflow so we can not use a local variable
bool ret = _tu_fifo_peek(f, buffer, f->wr_idx, f->rd_idx);
// Advance pointer
f->rd_idx = advance_pointer(f, f->rd_idx, ret);
@@ -685,10 +692,10 @@ uint16_t tu_fifo_read_n_const_addr_full_words(tu_fifo_t* f, void * buffer, uint1
@returns TRUE if the queue is not empty
*/
/******************************************************************************/
bool tu_fifo_peek_at(tu_fifo_t* f, uint16_t offset, void * p_buffer)
bool tu_fifo_peek(tu_fifo_t* f, void * p_buffer)
{
_ff_lock(f->mutex_rd);
bool ret = _tu_fifo_peek_at(f, offset, p_buffer, f->wr_idx, f->rd_idx);
bool ret = _tu_fifo_peek(f, p_buffer, f->wr_idx, f->rd_idx);
_ff_unlock(f->mutex_rd);
return ret;
}
@@ -700,8 +707,6 @@ bool tu_fifo_peek_at(tu_fifo_t* f, uint16_t offset, void * p_buffer)
@param[in] f
Pointer to the FIFO buffer to manipulate
@param[in] offset
Position to read from in the FIFO buffer with respect to read pointer
@param[in] p_buffer
Pointer to the place holder for data read from the buffer
@param[in] n
@@ -710,10 +715,10 @@ bool tu_fifo_peek_at(tu_fifo_t* f, uint16_t offset, void * p_buffer)
@returns Number of bytes written to p_buffer
*/
/******************************************************************************/
uint16_t tu_fifo_peek_at_n(tu_fifo_t* f, uint16_t offset, void * p_buffer, uint16_t n)
uint16_t tu_fifo_peek_n(tu_fifo_t* f, void * p_buffer, uint16_t n)
{
_ff_lock(f->mutex_rd);
bool ret = _tu_fifo_peek_at_n(f, offset, p_buffer, n, f->wr_idx, f->rd_idx, TU_FIFO_COPY_INC);
bool ret = _tu_fifo_peek_n(f, p_buffer, n, f->wr_idx, f->rd_idx, TU_FIFO_COPY_INC);
_ff_unlock(f->mutex_rd);
return ret;
}
@@ -742,7 +747,7 @@ bool tu_fifo_write(tu_fifo_t* f, const void * data)
if ( _tu_fifo_full(f, w, f->rd_idx) && !f->overwritable ) return false;
uint16_t wRel = get_relative_pointer(f, w, 0);
uint16_t wRel = get_relative_pointer(f, w);
// Write data
_ff_push(f, data, wRel);
@@ -883,36 +888,27 @@ void tu_fifo_advance_read_pointer(tu_fifo_t *f, uint16_t n)
/******************************************************************************/
/*!
@brief Get linear read info
@brief Get read info
Returns the length and pointer from which bytes can be read in a linear manner.
This is of major interest for DMA transmissions. If returned length is zero the
corresponding pointer is invalid. The returned length is limited to the number
of ITEMS n which the user wants to write into the buffer.
The write pointer does NOT get advanced, use tu_fifo_advance_read_pointer() to
do so! If the length returned is less than n i.e. len<n, then a wrap occurs
and you need to execute this function a second time to get a pointer to the
wrapped part!
corresponding pointer is invalid.
The read pointer does NOT get advanced, use tu_fifo_advance_read_pointer() to
do so!
@param[in] f
Pointer to FIFO
@param[in] offset
Number of ITEMS to ignore before start writing
@param[out] **ptr
Pointer to start writing to
@param[in] n
Number of ITEMS to read from buffer
@return len
Length of linear part IN ITEMS, if zero corresponding pointer ptr is invalid
@param[out] *info
Pointer to struct which holds the desired infos
*/
/******************************************************************************/
uint16_t tu_fifo_get_linear_read_info(tu_fifo_t *f, uint16_t offset, void **ptr, uint16_t n)
void tu_fifo_get_read_info(tu_fifo_t *f, tu_fifo_buffer_info_t *info)
{
// Operate on temporary values in case they change in between
uint16_t w = f->wr_idx, r = f->rd_idx;
uint16_t cnt = _tu_fifo_count(f, w, r);
// Check overflow and correct if required
// Check overflow and correct if required - may happen in case a DMA wrote too fast
if (cnt > f->depth)
{
_ff_lock(f->mutex_rd);
@@ -922,104 +918,85 @@ uint16_t tu_fifo_get_linear_read_info(tu_fifo_t *f, uint16_t offset, void **ptr,
cnt = f->depth;
}
// Skip beginning of buffer
if (cnt == 0 || offset >= cnt) return 0;
// Check if we can read something at and after offset - if too less is available we read what remains
cnt -= offset;
if (cnt < n) n = cnt;
// Check if fifo is empty
if (cnt == 0)
{
info->len_lin = 0;
info->len_wrap = 0;
info->ptr_lin = NULL;
info->ptr_wrap = NULL;
return;
}
// Get relative pointers
w = get_relative_pointer(f, w, 0);
r = get_relative_pointer(f, r, offset);
w = get_relative_pointer(f, w);
r = get_relative_pointer(f, r);
// Copy pointer to buffer to start reading from
info->ptr_lin = &f->buffer[r];
// Check if there is a wrap around necessary
uint16_t len;
if (w > r) {
len = w - r;
// Non wrapping case
info->len_lin = cnt;
info->len_wrap = 0;
info->ptr_wrap = NULL;
}
else
{
len = f->depth - r; // Also the case if FIFO was full
info->len_lin = f->depth - r; // Also the case if FIFO was full
info->len_wrap = cnt - info->len_lin;
info->ptr_wrap = f->buffer;
}
// Limit to required length
len = tu_min16(n, len);
// Copy pointer to buffer to start reading from
*ptr = &f->buffer[r];
return len;
}
/******************************************************************************/
/*!
@brief Get linear write info
Returns the length and pointer from which bytes can be written into buffer array in a linear manner.
This is of major interest for DMA transmissions not using circular mode. If returned length is zero the
corresponding pointer is invalid. The returned length is limited to the number of BYTES n which the user
wants to write into the buffer.
The write pointer does NOT get advanced, use tu_fifo_advance_write_pointer() to do so! If the length
returned is less than n i.e. len<n, then a wrap occurs and you need to execute this function a second
time to get a pointer to the wrapped part!
Returns the length and pointer to which bytes can be written into FIFO in a linear manner.
This is of major interest for DMA transmissions not using circular mode. If a returned length is zero the
corresponding pointer is invalid. The returned lengths summed up are the currently free space in the FIFO.
The write pointer does NOT get advanced, use tu_fifo_advance_write_pointer() to do so!
TAKE CARE TO NOT OVERFLOW THE BUFFER MORE THAN TWO TIMES THE FIFO DEPTH - IT CAN NOT RECOVERE OTHERWISE!
@param[in] f
Pointer to FIFO
@param[in] offset
Number of ITEMS to ignore before start writing
@param[out] **ptr
Pointer to start writing to
@param[in] n
Number of ITEMS to write into buffer
@return len
Length of linear part IN ITEMS, if zero corresponding pointer ptr is invalid
@param[out] *info
Pointer to struct which holds the desired infos
*/
/******************************************************************************/
uint16_t tu_fifo_get_linear_write_info(tu_fifo_t *f, uint16_t offset, void **ptr, uint16_t n)
void tu_fifo_get_write_info(tu_fifo_t *f, tu_fifo_buffer_info_t *info)
{
uint16_t w = f->wr_idx, r = f->rd_idx;
uint16_t free = _tu_fifo_remaining(f, w, r);
if (!f->overwritable)
if (free == 0)
{
// Not overwritable limit up to full
n = tu_min16(n, free);
info->len_lin = 0;
info->len_wrap = 0;
info->ptr_lin = NULL;
info->ptr_wrap = NULL;
return;
}
else if (n >= f->depth)
{
// If overwrite is allowed it must be less than or equal to 2 x buffer length, otherwise the overflow can not be resolved by the read functions
TU_VERIFY(n <= 2*f->depth);
n = f->depth;
// We start writing at the read pointer's position since we fill the complete
// buffer and we do not want to modify the read pointer within a write function!
// This would end up in a race condition with read functions!
w = r;
}
// Check if there is room to write to
if (free == 0 || offset >= free) return 0;
// Get relative pointers
w = get_relative_pointer(f, w, offset);
r = get_relative_pointer(f, r, 0);
uint16_t len;
w = get_relative_pointer(f, w);
r = get_relative_pointer(f, r);
// Copy pointer to buffer to start writing to
info->ptr_lin = &f->buffer[w];
if (w < r)
{
len = r-w;
// Non wrapping case
info->len_lin = r-w;
info->len_wrap = 0;
info->ptr_wrap = NULL;
}
else
{
len = f->depth - w;
info->len_lin = f->depth - w;
info->len_wrap = free - info->len_lin; // Remaining length - n already was limited to free or FIFO depth
info->ptr_wrap = f->buffer; // Always start of buffer
}
// Limit to required length
len = tu_min16(n, len);
// Copy pointer to buffer to start reading from
*ptr = &f->buffer[w];
return len;
}

View File

@@ -25,10 +25,6 @@
* This file is part of the TinyUSB stack.
*/
/** \ingroup Group_Common
* \defgroup group_fifo fifo
* @{ */
#ifndef _TUSB_FIFO_H_
#define _TUSB_FIFO_H_
@@ -62,16 +58,16 @@ extern "C" {
*/
typedef struct
{
uint8_t* buffer ; ///< buffer pointer
uint16_t depth ; ///< max items
uint16_t item_size ; ///< size of each item
bool overwritable ;
uint8_t* buffer ; ///< buffer pointer
uint16_t depth ; ///< max items
uint16_t item_size ; ///< size of each item
bool overwritable ;
uint16_t non_used_index_space ; ///< required for non-power-of-two buffer length
uint16_t max_pointer_idx ; ///< maximum absolute pointer index
uint16_t non_used_index_space ; ///< required for non-power-of-two buffer length
uint16_t max_pointer_idx ; ///< maximum absolute pointer index
volatile uint16_t wr_idx ; ///< write pointer
volatile uint16_t rd_idx ; ///< read pointer
volatile uint16_t wr_idx ; ///< write pointer
volatile uint16_t rd_idx ; ///< read pointer
#if CFG_FIFO_MUTEX
tu_fifo_mutex_t mutex_wr;
@@ -80,6 +76,14 @@ typedef struct
} tu_fifo_t;
typedef struct
{
uint16_t len_lin ; ///< linear length in item size
uint16_t len_wrap ; ///< wrapped length in item size
void * ptr_lin ; ///< linear part start pointer
void * ptr_wrap ; ///< wrapped part start pointer
} tu_fifo_buffer_info_t;
#define TU_FIFO_INIT(_buffer, _depth, _type, _overwritable) \
{ \
.buffer = _buffer, \
@@ -115,8 +119,8 @@ bool tu_fifo_read (tu_fifo_t* f, void * p_buffer);
uint16_t tu_fifo_read_n (tu_fifo_t* f, void * p_buffer, uint16_t n);
uint16_t tu_fifo_read_n_const_addr_full_words (tu_fifo_t* f, void * buffer, uint16_t n);
bool tu_fifo_peek_at (tu_fifo_t* f, uint16_t pos, void * p_buffer);
uint16_t tu_fifo_peek_at_n (tu_fifo_t* f, uint16_t pos, void * p_buffer, uint16_t n);
bool tu_fifo_peek (tu_fifo_t* f, void * p_buffer);
uint16_t tu_fifo_peek_n (tu_fifo_t* f, void * p_buffer, uint16_t n);
uint16_t tu_fifo_count (tu_fifo_t* f);
bool tu_fifo_empty (tu_fifo_t* f);
@@ -125,27 +129,22 @@ uint16_t tu_fifo_remaining (tu_fifo_t* f);
bool tu_fifo_overflowed (tu_fifo_t* f);
void tu_fifo_correct_read_pointer (tu_fifo_t* f);
static inline uint16_t tu_fifo_depth(tu_fifo_t* f)
{
return f->depth;
}
// Pointer modifications intended to be used in combinations with DMAs.
// USE WITH CARE - NO SAFTY CHECKS CONDUCTED HERE! NOT MUTEX PROTECTED!
void tu_fifo_advance_write_pointer (tu_fifo_t *f, uint16_t n);
void tu_fifo_advance_read_pointer (tu_fifo_t *f, uint16_t n);
// If you want to read/write from/to the FIFO by use of a DMA, you may need to conduct two copies to handle a possible wrapping part
// This functions deliver a pointer to start reading/writing from/to and a valid linear length along which no wrap occurs.
// In case not all of your data is available within one read/write, update the read/write pointer by
// tu_fifo_advance_read_pointer()/tu_fifo_advance_write_pointer and conduct a second read/write operation
uint16_t tu_fifo_get_linear_read_info (tu_fifo_t *f, uint16_t offset, void **ptr, uint16_t n);
uint16_t tu_fifo_get_linear_write_info (tu_fifo_t *f, uint16_t offset, void **ptr, uint16_t n);
// If you want to read/write from/to the FIFO by use of a DMA, you may need to conduct two copies
// to handle a possible wrapping part. These functions deliver a pointer to start
// reading/writing from/to and a valid linear length along which no wrap occurs.
void tu_fifo_get_read_info (tu_fifo_t *f, tu_fifo_buffer_info_t *info);
void tu_fifo_get_write_info(tu_fifo_t *f, tu_fifo_buffer_info_t *info);
static inline bool tu_fifo_peek(tu_fifo_t* f, void * p_buffer)
{
return tu_fifo_peek_at(f, 0, p_buffer);
}
static inline uint16_t tu_fifo_depth(tu_fifo_t* f)
{
return f->depth;
}
#ifdef __cplusplus
}