more tu_fifo refactor

This commit is contained in:
hathach
2025-11-21 14:51:54 +07:00
parent d4cdc096ca
commit d0d56a51a1
2 changed files with 81 additions and 138 deletions

View File

@ -70,10 +70,10 @@ bool tu_fifo_config(tu_fifo_t *f, void *buffer, uint16_t depth, uint16_t item_si
f->buffer = (uint8_t *)buffer;
f->depth = depth;
f->item_size = (uint16_t)(item_size & 0x7FFF);
f->item_size = (uint16_t)(item_size & 0x7FFFu);
f->overwritable = overwritable;
f->rd_idx = 0;
f->wr_idx = 0;
f->rd_idx = 0u;
f->wr_idx = 0u;
_ff_unlock(f->mutex_wr);
_ff_unlock(f->mutex_rd);
@ -85,7 +85,7 @@ bool tu_fifo_config(tu_fifo_t *f, void *buffer, uint16_t depth, uint16_t item_si
// Pull & Push
//--------------------------------------------------------------------+
#ifdef CFG_TUSB_FIFO_ACCESS_FIXED_ADDR_RW32
#ifdef CFG_TUSB_FIFO_ACCESS_FIXED_ADDR_RW32
// Intended to be used to read from hardware USB FIFO in e.g. STM32 where all data is read from a constant address
// Code adapted from dcd_synopsys.c
// TODO generalize with configurable 1 byte or 4 byte each read
@ -297,26 +297,6 @@ static void _ff_pull_n(tu_fifo_t *f, void *app_buf, uint16_t n, uint16_t rd_ptr,
}
}
//--------------------------------------------------------------------+
// Helper
//--------------------------------------------------------------------+
// return only the index difference and as such can be used to determine an overflow i.e overflowable count
TU_ATTR_ALWAYS_INLINE static inline uint16_t _ff_count(uint16_t depth, uint16_t wr_idx, uint16_t rd_idx) {
// In case we have non-power of two depth we need a further modification
if (wr_idx >= rd_idx) {
return (uint16_t)(wr_idx - rd_idx);
} else {
return (uint16_t)(2 * depth - (rd_idx - wr_idx));
}
}
// return remaining slot in fifo
TU_ATTR_ALWAYS_INLINE static inline uint16_t _ff_remaining(uint16_t depth, uint16_t wr_idx, uint16_t rd_idx) {
const uint16_t count = _ff_count(depth, wr_idx, rd_idx);
return (depth > count) ? (depth - count) : 0;
}
//--------------------------------------------------------------------+
// Index Helper
//--------------------------------------------------------------------+
@ -346,9 +326,9 @@ TU_ATTR_ALWAYS_INLINE static inline uint16_t idx2ptr(uint16_t depth, uint16_t id
}
// Works on local copies of w
// When an overwritable fifo is overflowed, rd_idx will be re-index so that it forms
// an full fifo i.e _ff_count() = depth
TU_ATTR_ALWAYS_INLINE static inline uint16_t _ff_correct_read_index(tu_fifo_t *f, uint16_t wr_idx) {
// When an overwritable fifo is overflowed, rd_idx will be re-index so that it forms a full fifo i.e
// tu_ff_overflow_count() = depth
TU_ATTR_ALWAYS_INLINE static inline uint16_t ff_correct_read_index(tu_fifo_t *f, uint16_t wr_idx) {
uint16_t rd_idx;
if (wr_idx >= f->depth) {
rd_idx = wr_idx - f->depth;
@ -364,7 +344,7 @@ TU_ATTR_ALWAYS_INLINE static inline uint16_t _ff_correct_read_index(tu_fifo_t *f
// 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(tu_fifo_t *f, void *p_buffer, uint16_t wr_idx, uint16_t rd_idx) {
uint16_t cnt = _ff_count(f->depth, wr_idx, rd_idx);
uint16_t cnt = tu_ff_overflow_count(f->depth, wr_idx, rd_idx);
// nothing to peek
if (cnt == 0) {
@ -373,7 +353,7 @@ static bool _tu_fifo_peek(tu_fifo_t *f, void *p_buffer, uint16_t wr_idx, uint16_
// Check overflow and correct if required
if (cnt > f->depth) {
rd_idx = _ff_correct_read_index(f, wr_idx);
rd_idx = ff_correct_read_index(f, wr_idx);
}
uint16_t rd_ptr = idx2ptr(f->depth, rd_idx);
@ -384,11 +364,15 @@ static bool _tu_fifo_peek(tu_fifo_t *f, void *p_buffer, uint16_t wr_idx, uint16_
return true;
}
//--------------------------------------------------------------------+
// Application API
//--------------------------------------------------------------------+
// Works on local copies of w and r
// Must be protected by mutexes since in case of an overflow read pointer gets modified
uint16_t tu_fifo_peek_n_access(tu_fifo_t *f, void *p_buffer, uint16_t n, uint16_t wr_idx, uint16_t rd_idx,
tu_fifo_access_mode_t access_mode) {
uint16_t cnt = _ff_count(f->depth, wr_idx, rd_idx);
uint16_t cnt = tu_ff_overflow_count(f->depth, wr_idx, rd_idx);
if (cnt == 0) {
return 0; // nothing to peek
@ -396,7 +380,7 @@ uint16_t tu_fifo_peek_n_access(tu_fifo_t *f, void *p_buffer, uint16_t n, uint16_
// Check overflow and correct if required
if (cnt > f->depth) {
rd_idx = _ff_correct_read_index(f, wr_idx);
rd_idx = ff_correct_read_index(f, wr_idx);
cnt = f->depth;
}
@ -422,13 +406,12 @@ uint16_t tu_fifo_write_n_access(tu_fifo_t *f, const void *data, uint16_t n, tu_f
const uint8_t *buf8 = (const uint8_t *)data;
TU_LOG(
TU_FIFO_DBG, "rd = %3u, wr = %3u, count = %3u, remain = %3u, n = %3u: ", rd_idx, wr_idx,
_ff_count(f->depth, wr_idx, rd_idx), _ff_remaining(f->depth, wr_idx, rd_idx), n);
TU_LOG(TU_FIFO_DBG, "rd = %3u, wr = %3u, count = %3u, remain = %3u, n = %3u: ", rd_idx, wr_idx,
_ff_count(f->depth, wr_idx, rd_idx), _ff_remaining(f->depth, wr_idx, rd_idx), n);
if (!f->overwritable) {
// limit up to full
const uint16_t remain = _ff_remaining(f->depth, wr_idx, rd_idx);
const uint16_t remain = tu_ff_remaining_local(f->depth, wr_idx, rd_idx);
n = tu_min16(n, remain);
} else {
// In over-writable mode, fifo_write() is allowed even when fifo is full. In such case,
@ -449,7 +432,7 @@ uint16_t tu_fifo_write_n_access(tu_fifo_t *f, const void *data, uint16_t n, tu_f
// We start writing at the read pointer's position since we fill the whole buffer
wr_idx = rd_idx;
} else {
const uint16_t overflowable_count = _ff_count(f->depth, wr_idx, rd_idx);
const uint16_t overflowable_count = tu_ff_overflow_count(f->depth, wr_idx, rd_idx);
if (overflowable_count + n >= 2 * f->depth) {
// Double overflowed
// Index is bigger than the allowed range [0,2*depth)
@ -498,92 +481,10 @@ uint16_t tu_fifo_read_n_access(tu_fifo_t *f, void *buffer, uint16_t n, tu_fifo_a
return n;
}
//--------------------------------------------------------------------+
// Application API
//--------------------------------------------------------------------+
/******************************************************************************/
/*!
@brief Get number of items in FIFO.
As this function only reads the read and write pointers once, this function is
reentrant and thus thread and ISR save without any mutexes. In case an
overflow occurred, this function return f.depth at maximum. Overflows are
checked and corrected for in the read functions!
@param[in] f
Pointer to the FIFO buffer to manipulate
@returns Number of items in FIFO
*/
/******************************************************************************/
uint16_t tu_fifo_count(const tu_fifo_t *f) {
return tu_min16(_ff_count(f->depth, f->wr_idx, f->rd_idx), f->depth);
}
/******************************************************************************/
/*!
@brief Check if FIFO is full.
As this function only reads the read and write pointers once, this function is
reentrant and thus thread and ISR save without any mutexes.
@param[in] f
Pointer to the FIFO buffer to manipulate
@returns Number of items in FIFO
*/
/******************************************************************************/
bool tu_fifo_full(const tu_fifo_t *f) {
return _ff_count(f->depth, f->wr_idx, f->rd_idx) >= f->depth;
}
/******************************************************************************/
/*!
@brief Get remaining space in FIFO.
As this function only reads the read and write pointers once, this function is
reentrant and thus thread and ISR save without any mutexes.
@param[in] f
Pointer to the FIFO buffer to manipulate
@returns Number of items in FIFO
*/
/******************************************************************************/
uint16_t tu_fifo_remaining(const tu_fifo_t *f) {
return _ff_remaining(f->depth, f->wr_idx, f->rd_idx);
}
/******************************************************************************/
/*!
@brief Check if overflow happened.
BE AWARE - THIS FUNCTION MIGHT NOT GIVE A CORRECT ANSWERE IN CASE WRITE POINTER "OVERFLOWS"
Only one overflow is allowed for this function to work e.g. if depth = 100, you must not
write more than 2*depth-1 items in one rush without updating write pointer. Otherwise
write pointer wraps and your pointer states are messed up. This can only happen if you
use DMAs, write functions do not allow such an error. Avoid such nasty things!
All reading functions (read, peek) check for overflows and correct read pointer on their own such
that latest items are read.
If required (e.g. for DMA use) you can also correct the read pointer by
tu_fifo_correct_read_pointer().
@param[in] f
Pointer to the FIFO buffer to manipulate
@returns True if overflow happened
*/
/******************************************************************************/
bool tu_fifo_overflowed(const tu_fifo_t *f) {
return _ff_count(f->depth, f->wr_idx, f->rd_idx) > f->depth;
}
// Only use in case tu_fifo_overflow() returned true!
void tu_fifo_correct_read_pointer(tu_fifo_t *f) {
_ff_lock(f->mutex_rd);
_ff_correct_read_index(f, f->wr_idx);
ff_correct_read_index(f, f->wr_idx);
_ff_unlock(f->mutex_rd);
}
@ -801,12 +702,12 @@ void tu_fifo_get_read_info(tu_fifo_t *f, tu_fifo_buffer_info_t *info) {
uint16_t wr_idx = f->wr_idx;
uint16_t rd_idx = f->rd_idx;
uint16_t cnt = _ff_count(f->depth, wr_idx, rd_idx);
uint16_t cnt = tu_ff_overflow_count(f->depth, wr_idx, rd_idx);
// Check overflow and correct if required - may happen in case a DMA wrote too fast
if (cnt > f->depth) {
_ff_lock(f->mutex_rd);
rd_idx = _ff_correct_read_index(f, wr_idx);
rd_idx = ff_correct_read_index(f, wr_idx);
_ff_unlock(f->mutex_rd);
cnt = f->depth;
@ -861,7 +762,7 @@ 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) {
uint16_t wr_idx = f->wr_idx;
uint16_t rd_idx = f->rd_idx;
uint16_t remain = _ff_remaining(f->depth, wr_idx, rd_idx);
uint16_t remain = tu_ff_remaining_local(f->depth, wr_idx, rd_idx);
if (remain == 0) {
info->len_lin = 0;

View File

@ -149,7 +149,7 @@ typedef enum {
} tu_fifo_access_mode_t;
//--------------------------------------------------------------------+
//
// Setup API
//--------------------------------------------------------------------+
bool tu_fifo_set_overwritable(tu_fifo_t *f, bool overwritable);
bool tu_fifo_clear(tu_fifo_t *f);
@ -165,52 +165,94 @@ void tu_fifo_config_mutex(tu_fifo_t *f, osal_mutex_t wr_mutex, osal_mutex_t rd_m
#define tu_fifo_config_mutex(_f, _wr_mutex, _rd_mutex)
#endif
//--------------------------------------------------------------------+
// Write API
//--------------------------------------------------------------------+
uint16_t tu_fifo_write_n_access(tu_fifo_t *f, const void *data, uint16_t n, tu_fifo_access_mode_t access_mode);
bool tu_fifo_write(tu_fifo_t *f, const void *data);
TU_ATTR_ALWAYS_INLINE static inline uint16_t tu_fifo_write_n(tu_fifo_t *f, const void *data, uint16_t n) {
return tu_fifo_write_n_access(f, data, n, TU_FIFO_INC_ADDR_RW8);
}
//--------------------------------------------------------------------+
// Read API
//--------------------------------------------------------------------+
uint16_t tu_fifo_read_n_access(tu_fifo_t *f, void *buffer, uint16_t n, tu_fifo_access_mode_t access_mode);
bool tu_fifo_read(tu_fifo_t *f, void *buffer);
TU_ATTR_ALWAYS_INLINE static inline uint16_t tu_fifo_read_n(tu_fifo_t *f, void *buffer, uint16_t n) {
return tu_fifo_read_n_access(f, buffer, n, TU_FIFO_INC_ADDR_RW8);
}
//--------------------------------------------------------------------+
// Peek API
//--------------------------------------------------------------------+
uint16_t tu_fifo_peek_n_access(tu_fifo_t *f, void *p_buffer, uint16_t n, uint16_t wr_idx, uint16_t rd_idx,
tu_fifo_access_mode_t access_mode);
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(const tu_fifo_t *f);
uint16_t tu_fifo_remaining(const tu_fifo_t *f);
bool tu_fifo_full(const tu_fifo_t *f);
bool tu_fifo_overflowed(const tu_fifo_t *f);
TU_ATTR_ALWAYS_INLINE static inline bool tu_fifo_empty(const tu_fifo_t *f) {
return f->wr_idx == f->rd_idx;
}
TU_ATTR_ALWAYS_INLINE static inline uint16_t tu_fifo_depth(const tu_fifo_t *f) {
return f->depth;
}
//--------------------------------------------------------------------+
// Index API
//--------------------------------------------------------------------+
void tu_fifo_correct_read_pointer(tu_fifo_t *f);
// Pointer modifications intended to be used in combinations with DMAs.
// USE WITH CARE - NO SAFETY 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);
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. 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_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);
//--------------------------------------------------------------------+
// Internal Helper Local
// work on local copies of read/write indices in order to only access them once for re-entrancy
//--------------------------------------------------------------------+
// return overflowable count (index difference), which can be used to determine both fifo count and an overflow state
TU_ATTR_ALWAYS_INLINE static inline uint16_t tu_ff_overflow_count(uint16_t depth, uint16_t wr_idx, uint16_t rd_idx) {
if (wr_idx >= rd_idx) {
return (uint16_t)(wr_idx - rd_idx);
} else {
return (uint16_t)(2 * depth - (rd_idx - wr_idx));
}
}
// return remaining slot in fifo
TU_ATTR_ALWAYS_INLINE static inline uint16_t tu_ff_remaining_local(uint16_t depth, uint16_t wr_idx, uint16_t rd_idx) {
const uint16_t ovf_count = tu_ff_overflow_count(depth, wr_idx, rd_idx);
return (depth > ovf_count) ? (depth - ovf_count) : 0;
}
//--------------------------------------------------------------------+
// State API
// Following functions are reentrant since they only access read/write indices once, therefore can be used in thread and
// ISRs context without the need of mutexes
//--------------------------------------------------------------------+
TU_ATTR_ALWAYS_INLINE static inline uint16_t tu_fifo_depth(const tu_fifo_t *f) {
return f->depth;
}
TU_ATTR_ALWAYS_INLINE static inline bool tu_fifo_empty(const tu_fifo_t *f) {
return f->wr_idx == f->rd_idx;
}
// return number of items in fifo, capped to fifo's depth
TU_ATTR_ALWAYS_INLINE static inline uint16_t tu_fifo_count(const tu_fifo_t *f) {
return tu_min16(tu_ff_overflow_count(f->depth, f->wr_idx, f->rd_idx), f->depth);
}
// check if fifo is full
TU_ATTR_ALWAYS_INLINE static inline bool tu_fifo_full(const tu_fifo_t *f) {
return tu_ff_overflow_count(f->depth, f->wr_idx, f->rd_idx) >= f->depth;
}
TU_ATTR_ALWAYS_INLINE static inline uint16_t tu_fifo_remaining(const tu_fifo_t *f) {
return tu_ff_remaining_local(f->depth, f->wr_idx, f->rd_idx);
}
#ifdef __cplusplus
}
#endif