Browse Source

Merge pull request #516 from PanRe/tusb_fifo_unmaksed_pointers

Tusb fifo unmaksed pointers
Ha Thach 5 years ago
parent
commit
0686bd9369
2 changed files with 468 additions and 143 deletions
  1. 420 101
      src/common/tusb_fifo.c
  2. 48 42
      src/common/tusb_fifo.h

+ 420 - 101
src/common/tusb_fifo.c

@@ -2,6 +2,7 @@
  * The MIT License (MIT)
  * The MIT License (MIT)
  *
  *
  * Copyright (c) 2019 Ha Thach (tinyusb.org)
  * Copyright (c) 2019 Ha Thach (tinyusb.org)
+ * Copyright (c) 2020 Reinhard Panhuber - rework to unmasked pointers
  *
  *
  * Permission is hereby granted, free of charge, to any person obtaining a copy
  * Permission is hereby granted, free of charge, to any person obtaining a copy
  * of this software and associated documentation files (the "Software"), to deal
  * of this software and associated documentation files (the "Software"), to deal
@@ -57,6 +58,8 @@ static void tu_fifo_unlock(tu_fifo_t *f)
 
 
 bool tu_fifo_config(tu_fifo_t *f, void* buffer, uint16_t depth, uint16_t item_size, bool overwritable)
 bool tu_fifo_config(tu_fifo_t *f, void* buffer, uint16_t depth, uint16_t item_size, bool overwritable)
 {
 {
+  if (depth > 0x8000) return false;               // Maximum depth is 2^15 items
+
   tu_fifo_lock(f);
   tu_fifo_lock(f);
 
 
   f->buffer = (uint8_t*) buffer;
   f->buffer = (uint8_t*) buffer;
@@ -64,55 +67,328 @@ bool tu_fifo_config(tu_fifo_t *f, void* buffer, uint16_t depth, uint16_t item_si
   f->item_size = item_size;
   f->item_size = item_size;
   f->overwritable = overwritable;
   f->overwritable = overwritable;
 
 
-  f->rd_idx = f->wr_idx = f->count = 0;
+  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)
+  f->non_used_index_space = 0xFFFF - f->max_pointer_idx;
+
+  f->rd_idx = f->wr_idx = 0;
 
 
   tu_fifo_unlock(f);
   tu_fifo_unlock(f);
 
 
   return true;
   return true;
 }
 }
 
 
+// Static functions are intended to work on local variables
+
 static inline uint16_t _ff_mod(uint16_t idx, uint16_t depth)
 static inline uint16_t _ff_mod(uint16_t idx, uint16_t depth)
 {
 {
-  return (idx < depth) ? idx : (idx-depth);
+  while ( idx >= depth) idx -= depth;
+  return idx;
 }
 }
 
 
-// retrieve data from fifo
-static inline void _ff_pull(tu_fifo_t* f, void * buffer, uint16_t n)
+// send one item to FIFO WITHOUT updating write pointer
+static inline void _ff_push(tu_fifo_t* f, void const * data, uint16_t wRel)
 {
 {
-  memcpy(buffer,
-         f->buffer + (f->rd_idx * f->item_size),
-         f->item_size*n);
+  memcpy(f->buffer + (wRel * f->item_size), data, f->item_size);
+}
+
+// send n items to FIFO WITHOUT updating write pointer
+static void _ff_push_n(tu_fifo_t* f, void const * data, uint16_t n, uint16_t wRel)
+{
+  if(wRel + n <= f->depth)  // Linear mode only
+  {
+    memcpy(f->buffer + (wRel * f->item_size), data, n*f->item_size);
+  }
+  else      // Wrap around
+  {
+    uint16_t nLin = f->depth - wRel;
 
 
-  f->rd_idx = _ff_mod(f->rd_idx + n, f->depth);
-  f->count -= n;
+    // Write data to linear part of buffer
+    memcpy(f->buffer + (wRel * f->item_size), data, nLin*f->item_size);
+
+    // Write data wrapped around
+    memcpy(f->buffer, data + nLin*f->item_size, (n - nLin) * f->item_size);
+  }
 }
 }
 
 
-// send data to fifo
-static inline void _ff_push(tu_fifo_t* f, void const * data, uint16_t n)
+// get one item from FIFO WITHOUT updating read pointer
+static inline void _ff_pull(tu_fifo_t* f, void * p_buffer, uint16_t rRel)
 {
 {
-  memcpy(f->buffer + (f->wr_idx * f->item_size),
-         data,
-         f->item_size*n);
+  memcpy(p_buffer, f->buffer + (rRel * f->item_size), f->item_size);
+}
 
 
-  f->wr_idx = _ff_mod(f->wr_idx + n, f->depth);
+// get n items from FIFO WITHOUT updating read pointer
+static void _ff_pull_n(tu_fifo_t* f, void * p_buffer, uint16_t n, uint16_t rRel)
+{
+  if(rRel + n <= f->depth)       // Linear mode only
+  {
+    memcpy(p_buffer, f->buffer + (rRel * f->item_size), n*f->item_size);
+  }
+  else      // Wrap around
+  {
+    uint16_t nLin = f->depth - rRel;
 
 
-  if (tu_fifo_full(f))
+    // Read data from linear part of buffer
+    memcpy(p_buffer, f->buffer + (rRel * f->item_size), nLin*f->item_size);
+
+    // Read data wrapped part
+    memcpy(p_buffer + nLin*f->item_size, f->buffer, (n - nLin) * f->item_size);
+  }
+}
+
+// Advance an absolute pointer
+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
+  if ((p > p + offset) || (p + offset > f->max_pointer_idx))
   {
   {
-    f->rd_idx = f->wr_idx; // keep the full state (rd == wr && count = depth)
+    p = (p + offset) + f->non_used_index_space;
   }
   }
   else
   else
   {
   {
-    f->count += n;
+    p += offset;
   }
   }
+  return p;
+}
+
+// Backward an absolute pointer
+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
+  if ((p < p - offset) || (p - offset > f->max_pointer_idx))
+  {
+    p = (p - offset) - f->non_used_index_space;
+  }
+  else
+  {
+    p -= offset;
+  }
+  return p;
+}
+
+// get relative from absolute pointer
+static uint16_t get_relative_pointer(tu_fifo_t* f, uint16_t p, uint16_t offset)
+{
+  return _ff_mod(advance_pointer(f, p, offset), f->depth);
+}
+
+// Works on local copies of w and r
+static inline uint16_t _tu_fifo_count(tu_fifo_t* f, uint16_t wAbs, uint16_t rAbs)
+{
+  uint16_t cnt = wAbs-rAbs;
+
+  // In case we have non-power of two depth we need a further modification
+  if (rAbs > wAbs) cnt -= f->non_used_index_space;
+
+  return cnt;
+}
+
+// Works on local copies of w and r
+static inline bool _tu_fifo_empty(uint16_t wAbs, uint16_t rAbs)
+{
+  return wAbs == rAbs;
+}
+
+// Works on local copies of w and r
+static inline bool _tu_fifo_full(tu_fifo_t* f, uint16_t wAbs, uint16_t rAbs)
+{
+  return (_tu_fifo_count(f, wAbs, rAbs) == f->depth);
+}
+
+// Works on local copies of w and r
+// 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 you pointer states are messed up. This can only happen if you
+// use DMAs, write functions do not allow such an error.
+static inline bool _tu_fifo_overflowed(tu_fifo_t* f, uint16_t wAbs, uint16_t rAbs)
+{
+  return (_tu_fifo_count(f, wAbs, rAbs) > f->depth);
+}
+
+// Works on local copies of w
+// For more details see _tu_fifo_overflow()!
+static inline void _tu_fifo_correct_read_pointer(tu_fifo_t* f, uint16_t wAbs)
+{
+  f->rd_idx = backward_pointer(f, wAbs, f->depth);
+}
+
+// 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)
+{
+  uint16_t cnt = _tu_fifo_count(f, wAbs, rAbs);
+
+  // Check overflow and correct if required
+  if (cnt > f->depth)
+  {
+    _tu_fifo_correct_read_pointer(f, wAbs);
+    cnt = f->depth;
+  }
+
+  // Skip beginning of buffer
+  if (cnt == 0 || offset >= cnt) return false;
+
+  uint16_t rRel = get_relative_pointer(f, rAbs, offset);
+
+  // Peek data
+  _ff_pull(f, p_buffer, rRel);
+
+  return true;
+}
+
+// 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)
+{
+  uint16_t cnt = _tu_fifo_count(f, wAbs, rAbs);
+
+  // Check overflow and correct if required
+  if (cnt > f->depth)
+  {
+    _tu_fifo_correct_read_pointer(f, wAbs);
+    rAbs = f->rd_idx;
+    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) {
+    if (cnt == 0) return 0;
+    n = cnt;
+  }
+
+  uint16_t rRel = get_relative_pointer(f, rAbs, offset);
+
+  // Peek data
+  _ff_pull_n(f, p_buffer, n, rRel);
+
+  return n;
+}
+
+// Works on local copies of w and r
+static inline uint16_t _tu_fifo_remaining(tu_fifo_t* f, uint16_t wAbs, uint16_t rAbs)
+{
+  return f->depth - _tu_fifo_count(f, wAbs, rAbs);
+}
+
+/******************************************************************************/
+/*!
+    @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.
+
+    @param[in]  f
+                Pointer to the FIFO buffer to manipulate
+
+    @returns Number of items in FIFO
+ */
+/******************************************************************************/
+uint16_t tu_fifo_count(tu_fifo_t* f)
+{
+  return _tu_fifo_count(f, f->wr_idx, f->rd_idx);
+}
+
+/******************************************************************************/
+/*!
+    @brief Check if FIFO is empty.
+
+    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_empty(tu_fifo_t* f)
+{
+  return _tu_fifo_empty(f->wr_idx, f->rd_idx);
+}
+
+/******************************************************************************/
+/*!
+    @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(tu_fifo_t* f)
+{
+  return _tu_fifo_full(f, f->wr_idx, f->rd_idx);
 }
 }
 
 
 /******************************************************************************/
 /******************************************************************************/
 /*!
 /*!
-    @brief Read one element out of the RX buffer.
+    @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(tu_fifo_t* f)
+{
+  return _tu_fifo_remaining(f, 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 you 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(tu_fifo_t* f)
+{
+  return _tu_fifo_overflowed(f, f->wr_idx, f->rd_idx);
+}
+
+// Only use in case tu_fifo_overflow() returned true!
+void tu_fifo_correct_read_pointer(tu_fifo_t* f)
+{
+  tu_fifo_lock(f);
+  _tu_fifo_correct_read_pointer(f, f->wr_idx);
+  tu_fifo_unlock(f);
+}
+
+/******************************************************************************/
+/*!
+    @brief Read one element out of the buffer.
 
 
     This function will return the element located at the array index of the
     This function will return the element located at the array index of the
-    read pointer, and then increment the read pointer index.  If the read
-    pointer exceeds the maximum buffer size, it will roll over to zero.
+    read pointer, and then increment the read pointer index.
+    This function checks for an overflow and corrects read pointer if required.
 
 
     @param[in]  f
     @param[in]  f
                 Pointer to the FIFO buffer to manipulate
                 Pointer to the FIFO buffer to manipulate
@@ -120,26 +396,27 @@ static inline void _ff_push(tu_fifo_t* f, void const * data, uint16_t n)
                 Pointer to the place holder for data read from the buffer
                 Pointer to the place holder for data read from the buffer
 
 
     @returns TRUE if the queue is not empty
     @returns TRUE if the queue is not empty
-*/
+ */
 /******************************************************************************/
 /******************************************************************************/
 bool tu_fifo_read(tu_fifo_t* f, void * buffer)
 bool tu_fifo_read(tu_fifo_t* f, void * buffer)
 {
 {
-  if( tu_fifo_empty(f) ) return false;
+  tu_fifo_lock(f);                                          // TODO: Here we may distinguish for read and write pointer mutexes!
 
 
-  tu_fifo_lock(f);
+  // 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
 
 
-  _ff_pull(f, buffer, 1);
+  // Advance pointer
+  f->rd_idx = advance_pointer(f, f->rd_idx, ret);
 
 
   tu_fifo_unlock(f);
   tu_fifo_unlock(f);
-
-  return true;
+  return ret;
 }
 }
 
 
 /******************************************************************************/
 /******************************************************************************/
 /*!
 /*!
     @brief This function will read n elements from the array index specified by
     @brief This function will read n elements from the array index specified by
-    the read pointer and increment the read index. If the read index
-    exceeds the max buffer size, then it will roll over to zero.
+    the read pointer and increment the read index.
+    This function checks for an overflow and corrects read pointer if required.
 
 
     @param[in]  f
     @param[in]  f
                 Pointer to the FIFO buffer to manipulate
                 Pointer to the FIFO buffer to manipulate
@@ -149,76 +426,76 @@ bool tu_fifo_read(tu_fifo_t* f, void * buffer)
                 Number of element that buffer can afford
                 Number of element that buffer can afford
 
 
     @returns number of items read from the FIFO
     @returns number of items read from the FIFO
-*/
+ */
 /******************************************************************************/
 /******************************************************************************/
-uint16_t tu_fifo_read_n (tu_fifo_t* f, void * buffer, uint16_t count)
+uint16_t tu_fifo_read_n(tu_fifo_t* f, void * buffer, uint16_t count)
 {
 {
-  if(tu_fifo_empty(f)) return 0;
-
-  tu_fifo_lock(f);
+  tu_fifo_lock(f);                                          // TODO: Here we may distinguish for read and write pointer mutexes!
 
 
-  // Limit up to fifo's count
-  if(count > f->count) count = f->count;
+  // Peek the data
+  count = _tu_fifo_peek_at_n(f, 0, buffer, count, 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
 
 
-  if(count + f->rd_idx <= f->depth)
-  {
-    _ff_pull(f, buffer, count);
-  }
-  else
-  {
-    uint16_t const part1 = f->depth - f->rd_idx;
-
-    // Part 1: from rd_idx to end
-    _ff_pull(f, buffer, part1);
-    buffer = ((uint8_t*) buffer) + part1*f->item_size;
-
-    // Part 2: start to remaining
-    _ff_pull(f, buffer, count-part1);
-  }
+  // Advance read pointer
+  f->rd_idx = advance_pointer(f, f->rd_idx, count);
 
 
   tu_fifo_unlock(f);
   tu_fifo_unlock(f);
-
   return count;
   return count;
 }
 }
 
 
 /******************************************************************************/
 /******************************************************************************/
 /*!
 /*!
-    @brief Read one item without removing it from the FIFO
+    @brief Read one item without removing it from the FIFO.
+    This function checks for an overflow and corrects read pointer if required.
 
 
     @param[in]  f
     @param[in]  f
                 Pointer to the FIFO buffer to manipulate
                 Pointer to the FIFO buffer to manipulate
-    @param[in]  pos
-                Position to read from in the FIFO buffer
+    @param[in]  offset
+                Position to read from in the FIFO buffer with respect to read pointer
     @param[in]  p_buffer
     @param[in]  p_buffer
                 Pointer to the place holder for data read from the buffer
                 Pointer to the place holder for data read from the buffer
 
 
     @returns TRUE if the queue is not empty
     @returns TRUE if the queue is not empty
-*/
+ */
 /******************************************************************************/
 /******************************************************************************/
-bool tu_fifo_peek_at(tu_fifo_t* f, uint16_t pos, void * p_buffer)
+bool tu_fifo_peek_at(tu_fifo_t* f, uint16_t offset, void * p_buffer)
 {
 {
-  if ( pos >= f->count ) return false;
+  tu_fifo_lock(f);                                          // TODO: Here we may distinguish for read and write pointer mutexes!
+  bool ret = _tu_fifo_peek_at(f, offset, p_buffer, f->wr_idx, f->rd_idx);
+  tu_fifo_unlock(f);
+  return ret;
+}
 
 
-  tu_fifo_lock(f);
+/******************************************************************************/
+/*!
+    @brief Read n items without removing it from the FIFO
+    This function checks for an overflow and corrects read pointer if required.
 
 
-  // rd_idx is pos=0
-  uint16_t index = _ff_mod(f->rd_idx + pos, f->depth);
-  memcpy(p_buffer,
-         f->buffer + (index * f->item_size),
-         f->item_size);
+    @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
+                Number of items to peek
 
 
+    @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)
+{
+  tu_fifo_lock(f);                                          // TODO: Here we may distinguish for read and write pointer mutexes!
+  bool ret = _tu_fifo_peek_at_n(f, offset, p_buffer, n, f->wr_idx, f->rd_idx);
   tu_fifo_unlock(f);
   tu_fifo_unlock(f);
-
-  return true;
+  return ret;
 }
 }
 
 
 /******************************************************************************/
 /******************************************************************************/
 /*!
 /*!
-    @brief Write one element into the RX buffer.
+    @brief Write one element into the buffer.
 
 
     This function will write one element into the array index specified by
     This function will write one element into the array index specified by
-    the write pointer and increment the write index. If the write index
-    exceeds the max buffer size, then it will roll over to zero.
+    the write pointer and increment the write index.
 
 
     @param[in]  f
     @param[in]  f
                 Pointer to the FIFO buffer to manipulate
                 Pointer to the FIFO buffer to manipulate
@@ -227,15 +504,23 @@ bool tu_fifo_peek_at(tu_fifo_t* f, uint16_t pos, void * p_buffer)
 
 
     @returns TRUE if the data was written to the FIFO (overwrittable
     @returns TRUE if the data was written to the FIFO (overwrittable
              FIFO will always return TRUE)
              FIFO will always return TRUE)
-*/
+ */
 /******************************************************************************/
 /******************************************************************************/
-bool tu_fifo_write (tu_fifo_t* f, const void * data)
+bool tu_fifo_write(tu_fifo_t* f, const void * data)
 {
 {
-  if ( tu_fifo_full(f) && !f->overwritable ) return false;
-
   tu_fifo_lock(f);
   tu_fifo_lock(f);
 
 
-  _ff_push(f, data, 1);
+  uint16_t w = f->wr_idx;
+
+  if ( _tu_fifo_full(f, w, f->rd_idx) && !f->overwritable ) return false;
+
+  uint16_t wRel = get_relative_pointer(f, w, 0);
+
+  // Write data
+  _ff_push(f, data, wRel);
+
+  // Advance pointer
+  f->wr_idx = advance_pointer(f, w, 1);
 
 
   tu_fifo_unlock(f);
   tu_fifo_unlock(f);
 
 
@@ -245,8 +530,7 @@ bool tu_fifo_write (tu_fifo_t* f, const void * data)
 /******************************************************************************/
 /******************************************************************************/
 /*!
 /*!
     @brief This function will write n elements into the array index specified by
     @brief This function will write n elements into the array index specified by
-    the write pointer and increment the write index. If the write index
-    exceeds the max buffer size, then it will roll over to zero.
+    the write pointer and increment the write index.
 
 
     @param[in]  f
     @param[in]  f
                 Pointer to the FIFO buffer to manipulate
                 Pointer to the FIFO buffer to manipulate
@@ -255,47 +539,42 @@ bool tu_fifo_write (tu_fifo_t* f, const void * data)
     @param[in]  count
     @param[in]  count
                 Number of element
                 Number of element
     @return Number of written elements
     @return Number of written elements
-*/
+ */
 /******************************************************************************/
 /******************************************************************************/
-uint16_t tu_fifo_write_n (tu_fifo_t* f, const void * data, uint16_t count)
+uint16_t tu_fifo_write_n(tu_fifo_t* f, const void * data, uint16_t count)
 {
 {
   if ( count == 0 ) return 0;
   if ( count == 0 ) return 0;
 
 
   tu_fifo_lock(f);
   tu_fifo_lock(f);
 
 
+  uint16_t w = f->wr_idx, r = f->rd_idx;
   uint8_t const* buf8 = (uint8_t const*) data;
   uint8_t const* buf8 = (uint8_t const*) data;
 
 
   if (!f->overwritable)
   if (!f->overwritable)
   {
   {
     // Not overwritable limit up to full
     // Not overwritable limit up to full
-    count = tu_min16(count, tu_fifo_remaining(f));
+    count = tu_min16(count, _tu_fifo_remaining(f, w, r));
   }
   }
   else if (count > f->depth)
   else if (count > f->depth)
   {
   {
     // Only copy last part
     // Only copy last part
     buf8 = buf8 + (count - f->depth) * f->item_size;
     buf8 = buf8 + (count - f->depth) * f->item_size;
     count = f->depth;
     count = f->depth;
-    f->wr_idx = 0;
-    f->rd_idx = 0;
-    f->count = 0;
-  }
 
 
-  if (count + f->wr_idx <= f->depth )
-  {
-    _ff_push(f, buf8, count);
+    // 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!
+    f->wr_idx = r;
   }
   }
-  else
-  {
-    uint16_t const part1 = f->depth - f->wr_idx;
 
 
-    // Part 1: from wr_idx to end
-    _ff_push(f, buf8, part1);
-    buf8 += part1*f->item_size;
+  uint16_t wRel = get_relative_pointer(f, w, 0);
+
+  // Write data
+  _ff_push_n(f, buf8, count, wRel);
+
+  // Advance pointer
+  f->wr_idx = advance_pointer(f, w, count);
 
 
-    // Part 2: start to remaining
-    _ff_push(f, buf8, count-part1);
-  }
-  
   tu_fifo_unlock(f);
   tu_fifo_unlock(f);
 
 
   return count;
   return count;
@@ -303,19 +582,59 @@ uint16_t tu_fifo_write_n (tu_fifo_t* f, const void * data, uint16_t count)
 
 
 /******************************************************************************/
 /******************************************************************************/
 /*!
 /*!
-    @brief Clear the fifo read and write pointers and set length to zero
+    @brief Clear the fifo read and write pointers
 
 
     @param[in]  f
     @param[in]  f
                 Pointer to the FIFO buffer to manipulate
                 Pointer to the FIFO buffer to manipulate
-*/
+ */
 /******************************************************************************/
 /******************************************************************************/
 bool tu_fifo_clear(tu_fifo_t *f)
 bool tu_fifo_clear(tu_fifo_t *f)
 {
 {
   tu_fifo_lock(f);
   tu_fifo_lock(f);
-
-  f->rd_idx = f->wr_idx = f->count = 0;
-
+  f->rd_idx = f->wr_idx = 0;
   tu_fifo_unlock(f);
   tu_fifo_unlock(f);
 
 
   return true;
   return true;
 }
 }
+
+/******************************************************************************/
+/*!
+    @brief Advance write pointer - intended to be used in combination with DMA.
+    It is possible to fill the FIFO by use of a DMA in circular mode. Within
+    DMA ISRs you may update the write pointer to be able to read from the FIFO.
+    As long as the DMA is the only process writing into the FIFO this is safe
+    to use.
+
+    USE WITH CARE - WE DO NOT CONDUCT SAFTY CHECKS HERE!
+
+    @param[in]  f
+                Pointer to the FIFO buffer to manipulate
+    @param[in]  n
+                Number of items the write pointer moves forward
+ */
+/******************************************************************************/
+void tu_fifo_advance_write_pointer(tu_fifo_t *f, uint16_t n)
+{
+  f->wr_idx = advance_pointer(f, f->wr_idx, n);
+}
+
+/******************************************************************************/
+/*!
+    @brief Advance read pointer - intended to be used in combination with DMA.
+    It is possible to read from the FIFO by use of a DMA in linear mode. Within
+    DMA ISRs you may update the read pointer to be able to again write into the
+    FIFO. As long as the DMA is the only process reading from the FIFO this is
+    safe to use.
+
+    USE WITH CARE - WE DO NOT CONDUCT SAFTY CHECKS HERE!
+
+    @param[in]  f
+                Pointer to the FIFO buffer to manipulate
+    @param[in]  n
+                Number of items the read pointer moves forward
+ */
+/******************************************************************************/
+void tu_fifo_advance_read_pointer(tu_fifo_t *f, uint16_t n)
+{
+  f->rd_idx = advance_pointer(f, f->rd_idx, n);
+}

+ 48 - 42
src/common/tusb_fifo.h

@@ -31,6 +31,15 @@
 #ifndef _TUSB_FIFO_H_
 #ifndef _TUSB_FIFO_H_
 #define _TUSB_FIFO_H_
 #define _TUSB_FIFO_H_
 
 
+// Due to the use of unmasked pointers, this FIFO does not suffer from loosing
+// one item slice. Furthermore, write and read operations are completely
+// decoupled as write and read functions do not modify a common state. Henceforth,
+// writing or reading from the FIFO within an ISR is safe as long as no other
+// process (thread or ISR) interferes.
+// Also, this FIFO is ready to be used in combination with a DMA as the write and
+// read pointers can be updated from within a DMA ISR. Overflows are detectable
+// within a certain number (see tu_fifo_overflow()).
+
 // mutex is only needed for RTOS
 // mutex is only needed for RTOS
 // for OS None, we don't get preempted
 // for OS None, we don't get preempted
 #define CFG_FIFO_MUTEX      (CFG_TUSB_OS != OPT_OS_NONE)
 #define CFG_FIFO_MUTEX      (CFG_TUSB_OS != OPT_OS_NONE)
@@ -39,7 +48,7 @@
 #include <stdbool.h>
 #include <stdbool.h>
 
 
 #ifdef __cplusplus
 #ifdef __cplusplus
- extern "C" {
+extern "C" {
 #endif
 #endif
 
 
 #if CFG_FIFO_MUTEX
 #if CFG_FIFO_MUTEX
@@ -52,14 +61,16 @@
  */
  */
 typedef struct
 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
 
 
-  volatile uint16_t count     ; ///< number of items in queue
-  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
 #if CFG_FIFO_MUTEX
   tu_fifo_mutex_t mutex;
   tu_fifo_mutex_t mutex;
@@ -67,14 +78,16 @@ typedef struct
 
 
 } tu_fifo_t;
 } tu_fifo_t;
 
 
-#define TU_FIFO_DEF(_name, _depth, _type, _overwritable) \
-  uint8_t _name##_buf[_depth*sizeof(_type)]; \
-  tu_fifo_t _name = {                        \
-      .buffer       = _name##_buf,           \
-      .depth        = _depth,                \
-      .item_size    = sizeof(_type),         \
-      .overwritable = _overwritable,         \
-  }
+#define TU_FIFO_DEF(_name, _depth, _type, _overwritable)                \
+    uint8_t _name##_buf[_depth*sizeof(_type)];                          \
+    tu_fifo_t _name = {                                                 \
+        .buffer                 = _name##_buf,                          \
+        .depth                  = _depth,                               \
+        .item_size              = sizeof(_type),                        \
+        .overwritable           = _overwritable,                        \
+        .max_pointer_idx        = 2*_depth-1,                           \
+        .non_used_index_space   = 0xFFFF - 2*_depth-1,                  \
+    }
 
 
 bool tu_fifo_clear(tu_fifo_t *f);
 bool tu_fifo_clear(tu_fifo_t *f);
 bool tu_fifo_config(tu_fifo_t *f, void* buffer, uint16_t depth, uint16_t item_size, bool overwritable);
 bool tu_fifo_config(tu_fifo_t *f, void* buffer, uint16_t depth, uint16_t item_size, bool overwritable);
@@ -86,37 +99,30 @@ static inline void tu_fifo_config_mutex(tu_fifo_t *f, tu_fifo_mutex_t mutex_hdl)
 }
 }
 #endif
 #endif
 
 
-bool     tu_fifo_write   (tu_fifo_t* f, void const * p_data);
-uint16_t tu_fifo_write_n (tu_fifo_t* f, void const * p_data, uint16_t count);
+bool     tu_fifo_write                  (tu_fifo_t* f, void const * p_data);
+uint16_t tu_fifo_write_n                (tu_fifo_t* f, void const * p_data, uint16_t count);
 
 
-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 count);
+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 count);
 
 
-bool     tu_fifo_peek_at (tu_fifo_t* f, uint16_t pos, void * p_buffer);
+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);
 
 
-static inline bool tu_fifo_peek(tu_fifo_t* f, void * p_buffer)
-{
-  return tu_fifo_peek_at(f, 0, p_buffer);
-}
+uint16_t tu_fifo_count                  (tu_fifo_t* f);
+bool     tu_fifo_empty                  (tu_fifo_t* f);
+bool     tu_fifo_full                   (tu_fifo_t* f);
+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 bool tu_fifo_empty(tu_fifo_t* f)
-{
-  return (f->count == 0);
-}
+// 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);
 
 
-static inline bool tu_fifo_full(tu_fifo_t* f)
-{
-  return (f->count == f->depth);
-}
-
-static inline uint16_t tu_fifo_count(tu_fifo_t* f)
-{
-  return f->count;
-}
-
-static inline uint16_t tu_fifo_remaining(tu_fifo_t* f)
+static inline bool tu_fifo_peek(tu_fifo_t* f, void * p_buffer)
 {
 {
-  return f->depth - f->count;
+  return tu_fifo_peek_at(f, 0, p_buffer);
 }
 }
 
 
 static inline uint16_t tu_fifo_depth(tu_fifo_t* f)
 static inline uint16_t tu_fifo_depth(tu_fifo_t* f)
@@ -125,7 +131,7 @@ static inline uint16_t tu_fifo_depth(tu_fifo_t* f)
 }
 }
 
 
 #ifdef __cplusplus
 #ifdef __cplusplus
- }
+}
 #endif
 #endif
 
 
 #endif /* _TUSB_FIFO_H_ */
 #endif /* _TUSB_FIFO_H_ */