diff --git a/src/common/tusb_fifo.c b/src/common/tusb_fifo.c index a386273f0..81e11eb01 100644 --- a/src/common/tusb_fifo.c +++ b/src/common/tusb_fifo.c @@ -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; @@ -496,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_n(f, 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); @@ -634,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(f, 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); @@ -910,12 +918,12 @@ void tu_fifo_get_read_info(tu_fifo_t *f, tu_fifo_buffer_info_t *info) cnt = f->depth; } - // Skip beginning of buffer + // Check if fifo is empty if (cnt == 0) { - info->len_lin = 0; + info->len_lin = 0; info->len_wrap = 0; - info->ptr_lin = NULL; + info->ptr_lin = NULL; info->ptr_wrap = NULL; return; } @@ -930,18 +938,16 @@ void tu_fifo_get_read_info(tu_fifo_t *f, tu_fifo_buffer_info_t *info) // Check if there is a wrap around necessary if (w > r) { // Non wrapping case - info->len_lin = cnt; + info->len_lin = cnt; info->len_wrap = 0; info->ptr_wrap = NULL; } else { - info->len_lin = 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; } - - return; } /******************************************************************************/ @@ -957,8 +963,6 @@ void tu_fifo_get_read_info(tu_fifo_t *f, tu_fifo_buffer_info_t *info) Pointer to FIFO @param[out] *info Pointer to struct which holds the desired infos - @param[in] n - Number of ITEMS to write into buffer */ /******************************************************************************/ void tu_fifo_get_write_info(tu_fifo_t *f, tu_fifo_buffer_info_t *info) @@ -985,16 +989,14 @@ void tu_fifo_get_write_info(tu_fifo_t *f, tu_fifo_buffer_info_t *info) if (w < r) { // Non wrapping case - info->len_lin = r-w; // Limit to required length + info->len_lin = r-w; info->len_wrap = 0; info->ptr_wrap = NULL; } else { 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 + 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 } - - return; } diff --git a/src/common/tusb_fifo.h b/src/common/tusb_fifo.h index f8cc282d3..8d73911f0 100644 --- a/src/common/tusb_fifo.h +++ b/src/common/tusb_fifo.h @@ -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; @@ -82,10 +78,10 @@ typedef struct 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 + 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) \ @@ -133,21 +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. - -void tu_fifo_get_read_info(tu_fifo_t *f, tu_fifo_buffer_info_t *info); +// 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 uint16_t tu_fifo_depth(tu_fifo_t* f) -{ - return f->depth; -} #ifdef __cplusplus } diff --git a/test/test/test_fifo.c b/test/test/test_fifo.c index 5ea8de512..f9bfc5f03 100644 --- a/test/test/test_fifo.c +++ b/test/test/test_fifo.c @@ -24,15 +24,19 @@ * This file is part of the TinyUSB stack. */ +#include #include "unity.h" #include "tusb_fifo.h" #define FIFO_SIZE 10 -TU_FIFO_DEF(ff, FIFO_SIZE, uint8_t, false); +TU_FIFO_DEF(tu_ff, FIFO_SIZE, uint8_t, false); +tu_fifo_t* ff = &tu_ff; +tu_fifo_buffer_info_t info; void setUp(void) { - tu_fifo_clear(&ff); + tu_fifo_clear(ff); + memset(&info, 0, sizeof(tu_fifo_buffer_info_t)); } void tearDown(void) @@ -44,12 +48,12 @@ void tearDown(void) //--------------------------------------------------------------------+ void test_normal(void) { - for(uint8_t i=0; i < FIFO_SIZE; i++) tu_fifo_write(&ff, &i); + for(uint8_t i=0; i < FIFO_SIZE; i++) tu_fifo_write(ff, &i); for(uint8_t i=0; i < FIFO_SIZE; i++) { uint8_t c; - tu_fifo_read(&ff, &c); + tu_fifo_read(ff, &c); TEST_ASSERT_EQUAL(i, c); } } @@ -86,30 +90,30 @@ void test_read_n(void) uint8_t data[20]; for(int i=0; i 4 - rd_count = tu_fifo_read_n(&ff, rd, 5); + rd_count = tu_fifo_read_n(ff, rd, 5); TEST_ASSERT_EQUAL( 5, rd_count ); TEST_ASSERT_EQUAL_MEMORY( data, rd, rd_count ); // 0 -> 4 // case 2: Read index + count > depth // write 10, 11, 12 - tu_fifo_write(&ff, data+10); - tu_fifo_write(&ff, data+11); - tu_fifo_write(&ff, data+12); + tu_fifo_write(ff, data+10); + tu_fifo_write(ff, data+11); + tu_fifo_write(ff, data+12); - rd_count = tu_fifo_read_n(&ff, rd, 7); + rd_count = tu_fifo_read_n(ff, rd, 7); TEST_ASSERT_EQUAL( 7, rd_count ); TEST_ASSERT_EQUAL_MEMORY( data+5, rd, rd_count ); // 5 -> 11 // Should only read until empty - TEST_ASSERT_EQUAL( 1, tu_fifo_read_n(&ff, rd, 100) ); + TEST_ASSERT_EQUAL( 1, tu_fifo_read_n(ff, rd, 100) ); } void test_write_n(void) @@ -119,52 +123,172 @@ void test_write_n(void) for(int i=0; i 4 // case 2: wr + count > depth - tu_fifo_write_n(&ff, data+8, 6); // wr = 3, count = 9 + tu_fifo_write_n(ff, data+8, 6); // wr = 3, count = 9 - for(rd_count=0; rd_count<7; rd_count++) tu_fifo_read(&ff, rd+rd_count); // wr = 3, count = 2 + for(rd_count=0; rd_count<7; rd_count++) tu_fifo_read(ff, rd+rd_count); // wr = 3, count = 2 TEST_ASSERT_EQUAL_MEMORY( data+5, rd, rd_count); // 5 -> 11 - TEST_ASSERT_EQUAL(2, tu_fifo_count(&ff)); + TEST_ASSERT_EQUAL(2, tu_fifo_count(ff)); } void test_peek(void) { uint8_t temp; - temp = 10; tu_fifo_write(&ff, &temp); - temp = 20; tu_fifo_write(&ff, &temp); - temp = 30; tu_fifo_write(&ff, &temp); + temp = 10; tu_fifo_write(ff, &temp); + temp = 20; tu_fifo_write(ff, &temp); + temp = 30; tu_fifo_write(ff, &temp); temp = 0; - tu_fifo_peek(&ff, &temp); + tu_fifo_peek(ff, &temp); TEST_ASSERT_EQUAL(10, temp); + + tu_fifo_read(ff, &temp); + tu_fifo_read(ff, &temp); + + tu_fifo_peek(ff, &temp); + TEST_ASSERT_EQUAL(30, temp); +} + +void test_get_read_info_when_no_wrap() +{ + uint8_t ch = 1; + + // write 6 items + for(uint8_t i=0; i < 6; i++) tu_fifo_write(ff, &ch); + + // read 2 items + tu_fifo_read(ff, &ch); + tu_fifo_read(ff, &ch); + + tu_fifo_get_read_info(ff, &info); + + TEST_ASSERT_EQUAL(4, info.len_lin); + TEST_ASSERT_EQUAL(0, info.len_wrap); + + TEST_ASSERT_EQUAL_PTR(ff->buffer+2, info.ptr_lin); + TEST_ASSERT_NULL(info.ptr_wrap); +} + +void test_get_read_info_when_wrapped() +{ + uint8_t ch = 1; + + // make fifo full + for(uint8_t i=0; i < FIFO_SIZE; i++) tu_fifo_write(ff, &ch); + + // read 6 items + for(uint8_t i=0; i < 6; i++) tu_fifo_read(ff, &ch); + + // write 2 items + tu_fifo_write(ff, &ch); + tu_fifo_write(ff, &ch); + + tu_fifo_get_read_info(ff, &info); + + TEST_ASSERT_EQUAL(FIFO_SIZE-6, info.len_lin); + TEST_ASSERT_EQUAL(2, info.len_wrap); + + TEST_ASSERT_EQUAL_PTR(ff->buffer+6, info.ptr_lin); + TEST_ASSERT_EQUAL_PTR(ff->buffer, info.ptr_wrap); +} + +void test_get_write_info_when_no_wrap() +{ + uint8_t ch = 1; + + // write 2 items + tu_fifo_write(ff, &ch); + tu_fifo_write(ff, &ch); + + tu_fifo_get_write_info(ff, &info); + + TEST_ASSERT_EQUAL(FIFO_SIZE-2, info.len_lin); + TEST_ASSERT_EQUAL(0, info.len_wrap); + + TEST_ASSERT_EQUAL_PTR(ff->buffer+2, info .ptr_lin); + // application should check len instead of ptr. + // TEST_ASSERT_NULL(info.ptr_wrap); +} + +void test_get_write_info_when_wrapped() +{ + uint8_t ch = 1; + + // write 6 items + for(uint8_t i=0; i < 6; i++) tu_fifo_write(ff, &ch); + + // read 2 items + tu_fifo_read(ff, &ch); + tu_fifo_read(ff, &ch); + + tu_fifo_get_write_info(ff, &info); + + TEST_ASSERT_EQUAL(FIFO_SIZE-6, info.len_lin); + TEST_ASSERT_EQUAL(2, info.len_wrap); + + TEST_ASSERT_EQUAL_PTR(ff->buffer+6, info .ptr_lin); + TEST_ASSERT_EQUAL_PTR(ff->buffer, info.ptr_wrap); } void test_empty(void) { uint8_t temp; - TEST_ASSERT_TRUE(tu_fifo_empty(&ff)); - tu_fifo_write(&ff, &temp); - TEST_ASSERT_FALSE(tu_fifo_empty(&ff)); + TEST_ASSERT_TRUE(tu_fifo_empty(ff)); + + // read info + tu_fifo_get_read_info(ff, &info); + + TEST_ASSERT_EQUAL(0, info.len_lin); + TEST_ASSERT_EQUAL(0, info.len_wrap); + + TEST_ASSERT_NULL(info.ptr_lin); + TEST_ASSERT_NULL(info.ptr_wrap); + + // write info + tu_fifo_get_write_info(ff, &info); + + TEST_ASSERT_EQUAL(FIFO_SIZE, info.len_lin); + TEST_ASSERT_EQUAL(0, info.len_wrap); + + TEST_ASSERT_EQUAL_PTR(ff->buffer, info .ptr_lin); + // application should check len instead of ptr. + // TEST_ASSERT_NULL(info.ptr_wrap); + + // write 1 then re-check empty + tu_fifo_write(ff, &temp); + TEST_ASSERT_FALSE(tu_fifo_empty(ff)); } void test_full(void) { - TEST_ASSERT_FALSE(tu_fifo_full(&ff)); + TEST_ASSERT_FALSE(tu_fifo_full(ff)); - for(uint8_t i=0; i < FIFO_SIZE; i++) tu_fifo_write(&ff, &i); + for(uint8_t i=0; i < FIFO_SIZE; i++) tu_fifo_write(ff, &i); - TEST_ASSERT_TRUE(tu_fifo_full(&ff)); + TEST_ASSERT_TRUE(tu_fifo_full(ff)); + + // read info + tu_fifo_get_read_info(ff, &info); + + TEST_ASSERT_EQUAL(FIFO_SIZE, info.len_lin); + TEST_ASSERT_EQUAL(0, info.len_wrap); + + TEST_ASSERT_EQUAL_PTR(ff->buffer, info.ptr_lin); + // skip this, application must check len instead of buffer + // TEST_ASSERT_NULL(info.ptr_wrap); + + // write info }