support multiple data stride if configured

This commit is contained in:
hathach
2026-01-02 15:55:00 +07:00
parent 0b638c5d74
commit 36e8f9d7a1
5 changed files with 209 additions and 100 deletions

View File

@ -348,7 +348,7 @@ TU_ATTR_ALWAYS_INLINE static inline uint32_t tu_scatter_read32(const uint8_t *bu
return result;
}
// scatter write 4 bytes to two buffers. Parameter are not checked
// scatter write 4 bytes (LE) to two buffers. Parameter are not checked
TU_ATTR_ALWAYS_INLINE static inline void tu_scatter_write32(uint32_t value, uint8_t *buf1, uint8_t len1,
uint8_t *buf2, uint8_t len2) {
for (uint8_t i = 0; i < len1; ++i) {

View File

@ -114,119 +114,136 @@ void tu_fifo_set_overwritable(tu_fifo_t *f, bool overwritable) {
// copy data to/from fifo without updating read/write pointers
//--------------------------------------------------------------------+
#if CFG_TUSB_FIFO_HWFIFO_API
#if CFG_TUSB_FIFO_HWFIFO_DATA_STRIDE == 4
#define stride_unaligned_write tu_unaligned_write32
#define stride_unaligned_read tu_unaligned_read32
typedef uint32_t hwfifo_item_t;
#elif CFG_TUSB_FIFO_HWFIFO_DATA_STRIDE == 2
#define stride_unaligned_write tu_unaligned_write16
#define stride_unaligned_read tu_unaligned_read16
typedef uint16_t hwfifo_item_t;
#endif
enum {
STRIDE_REMAIN_MASK = sizeof(hwfifo_item_t) - 1u
};
void tu_hwfifo_read(const volatile void *hwfifo, uint8_t *dest, uint16_t len) {
const volatile hwfifo_item_t *src = (const volatile hwfifo_item_t *)hwfifo;
// Reading full available 16/32-bit hwfifo and write to fifo
while (len >= sizeof(hwfifo_item_t)) {
const hwfifo_item_t tmp = *src;
stride_unaligned_write(dest, tmp);
dest += sizeof(hwfifo_item_t);
len -= sizeof(hwfifo_item_t);
#if CFG_TUSB_FIFO_HWFIFO_ADDR_STRIDE
src = (const volatile hwfifo_item_t *)((uintptr_t)src + CFG_TUSB_FIFO_HWFIFO_ADDR_STRIDE);
#define HWFIFO_ADDR_NEXT(_const, _hwfifo) \
_hwfifo = (_const volatile void *)((uintptr_t)(_hwfifo) + CFG_TUSB_FIFO_HWFIFO_ADDR_STRIDE)
#else
#define HWFIFO_ADDR_NEXT(_const, _hwfifo)
#endif
static void stride_write(volatile void *hwfifo, const void *src, uint8_t data_stride) {
#if CFG_TUSB_FIFO_HWFIFO_DATA_STRIDE & 4
if (data_stride == 4) {
*((volatile uint32_t *)hwfifo) = tu_unaligned_read32(src);
}
#endif
#if CFG_TUSB_FIFO_HWFIFO_DATA_STRIDE & 2
if (data_stride == 2) {
*((volatile uint16_t *)hwfifo) = tu_unaligned_read16(src);
}
#endif
}
static void stride_read(const volatile void *hwfifo, void *dest, uint8_t data_stride) {
#if CFG_TUSB_FIFO_HWFIFO_DATA_STRIDE & 4
if (data_stride == 4) {
tu_unaligned_write32(dest, *((const volatile uint32_t *)hwfifo));
}
#endif
#if CFG_TUSB_FIFO_HWFIFO_DATA_STRIDE & 2
if (data_stride == 2) {
tu_unaligned_write16(dest, *((const volatile uint16_t *)hwfifo));
}
#endif
}
void tu_hwfifo_read_access_mode(const volatile void *hwfifo, uint8_t *dest, uint16_t len, uint8_t data_stride) {
// Reading full available 16/32-bit hwfifo and write to fifo
while (len >= data_stride) {
stride_read(hwfifo, dest, data_stride);
dest += data_stride;
len -= data_stride;
HWFIFO_ADDR_NEXT(const, hwfifo);
}
// Read odd bytes i.e 1 byte for 16 bit or 1-3 bytes for 32 bit
if (len > 0) {
#ifdef CFG_TUSB_FIFO_HWFIFO_DATA_STRIDE_ODD_BYTE_SUPPORT
// odd byte access, read byte per byte e.g for rusb2. No address stride needed
const volatile uint8_t *src8 = (const volatile uint8_t *)src;
const volatile uint8_t *src8 = (const volatile uint8_t *)hwfifo;
for (uint16_t i = 0; i < len; ++i) {
dest[i] = *src8;
dest[i] = *(src8 + 3);
}
#else
const hwfifo_item_t tmp = *src;
uint32_t tmp;
stride_read(hwfifo, &tmp, data_stride);
memcpy(dest, &tmp, len);
#endif
}
}
// Copy from fifo to fixed address buffer (usually a tx register) with TU_FIFO_FIXED_ADDR_RW32 mode
void tu_hwfifo_write(volatile void *hwfifo, const uint8_t *src, uint16_t len) {
volatile hwfifo_item_t *dest = (volatile hwfifo_item_t *)hwfifo;
void tu_hwfifo_write_access_mode(volatile void *hwfifo, const uint8_t *src, uint16_t len, uint8_t data_stride) {
// Write full available 16/32 bit words to dest
while (len >= sizeof(hwfifo_item_t)) {
*dest = stride_unaligned_read(src);
src += sizeof(hwfifo_item_t);
len -= sizeof(hwfifo_item_t);
while (len >= data_stride) {
stride_write(hwfifo, src, data_stride);
src += data_stride;
len -= data_stride;
#if CFG_TUSB_FIFO_HWFIFO_ADDR_STRIDE
dest = (volatile hwfifo_item_t *)((uintptr_t)dest + CFG_TUSB_FIFO_HWFIFO_ADDR_STRIDE);
#endif
HWFIFO_ADDR_NEXT(, hwfifo);
}
// Write odd bytes i.e 1 byte for 16 bit or 1-3 bytes for 32 bit
if (len > 0) {
#ifdef CFG_TUSB_FIFO_HWFIFO_DATA_STRIDE_ODD_BYTE_SUPPORT
// odd byte access, write byte per byte e.g for rusb2. No address stride needed
volatile uint8_t *dest8 = (volatile uint8_t *)dest;
volatile uint8_t *dest8 = (volatile uint8_t *)hwfifo;
for (uint16_t i = 0; i < len; ++i) {
*dest8 = src[i];
*(dest8 + 3) = src[i];
}
#else
hwfifo_item_t tmp = 0u;
uint32_t tmp = 0u;
memcpy(&tmp, src, len);
*dest = tmp;
stride_write(hwfifo, &tmp, data_stride);
#endif
}
}
#endif
// send n items to fifo WITHOUT updating write pointer
static void ff_push_n(const tu_fifo_t *f, const void *app_buf, uint16_t n, uint16_t wr_ptr, bool stride_mode) {
(void)stride_mode;
static void ff_push_n(const tu_fifo_t *f, const void *app_buf, uint16_t n, uint16_t wr_ptr, uint8_t data_stride) {
(void)data_stride;
uint16_t lin_bytes = f->depth - wr_ptr;
uint16_t wrap_bytes = n - lin_bytes;
uint8_t *ff_buf = f->buffer + wr_ptr;
#if CFG_TUSB_FIFO_HWFIFO_API
if (stride_mode) {
const volatile hwfifo_item_t *hwfifo = (const volatile hwfifo_item_t *)app_buf;
if (data_stride) {
const volatile void *hwfifo = (const volatile void *)app_buf;
if (n <= lin_bytes) {
// Linear only case
tu_hwfifo_read(hwfifo, ff_buf, n);
tu_hwfifo_read_access_mode(hwfifo, ff_buf, n, data_stride);
} else {
// Wrap around case
// Write full words to linear part of buffer
uint16_t lin_nitems_bytes = lin_bytes & ~STRIDE_REMAIN_MASK;
tu_hwfifo_read(hwfifo, ff_buf, lin_nitems_bytes);
ff_buf += lin_nitems_bytes;
const uint32_t odd_mask = data_stride - 1;
uint16_t lin_even = lin_bytes & ~odd_mask;
tu_hwfifo_read_access_mode(hwfifo, ff_buf, lin_even, data_stride);
ff_buf += lin_even;
// There could be odd 1 byte (16bit) or 1-3 bytes (32bit) before the wrap-around boundary
const uint8_t rem = lin_bytes & STRIDE_REMAIN_MASK;
if (rem > 0) {
const uint8_t remrem = (uint8_t)tu_min16(wrap_bytes, sizeof(hwfifo_item_t) - rem);
const hwfifo_item_t tmp = *hwfifo;
tu_scatter_write32(tmp, ff_buf, rem, f->buffer, remrem);
// combine it with the wrapped part to form a full word for data stride
const uint8_t lin_odd = lin_bytes & odd_mask;
if (lin_odd > 0) {
const uint8_t wrap_odd = (uint8_t)tu_min16(wrap_bytes, data_stride - lin_odd);
uint32_t tmp = 0;
stride_read(hwfifo, &tmp, data_stride);
HWFIFO_ADDR_NEXT(const, hwfifo);
wrap_bytes -= remrem;
ff_buf = f->buffer + remrem; // wrap around
tu_scatter_write32(tmp, ff_buf, lin_odd, f->buffer, wrap_odd);
wrap_bytes -= wrap_odd;
ff_buf = f->buffer + wrap_odd; // wrap around
} else {
ff_buf = f->buffer; // wrap around to beginning
ff_buf = f->buffer; // wrap around to beginning
}
// Write data wrapped part
if (wrap_bytes > 0) {
tu_hwfifo_read(hwfifo, ff_buf, wrap_bytes);
tu_hwfifo_read_access_mode(hwfifo, ff_buf, wrap_bytes, data_stride);
}
}
} else
@ -245,44 +262,46 @@ static void ff_push_n(const tu_fifo_t *f, const void *app_buf, uint16_t n, uint1
}
// get n items from fifo WITHOUT updating read pointer
static void ff_pull_n(const tu_fifo_t *f, void *app_buf, uint16_t n, uint16_t rd_ptr, bool stride_mode) {
(void)stride_mode;
static void ff_pull_n(const tu_fifo_t *f, void *app_buf, uint16_t n, uint16_t rd_ptr, uint8_t data_stride) {
(void)data_stride;
uint16_t lin_bytes = f->depth - rd_ptr;
uint16_t wrap_bytes = n - lin_bytes; // only used if wrapped
const uint8_t *ff_buf = f->buffer + rd_ptr;
#if CFG_TUSB_FIFO_HWFIFO_API
if (stride_mode) {
volatile hwfifo_item_t *hwfifo = (volatile hwfifo_item_t *)app_buf;
if (data_stride) {
volatile void *hwfifo = (volatile void *)app_buf;
if (n <= lin_bytes) {
// Linear only case
tu_hwfifo_write(hwfifo, ff_buf, n);
tu_hwfifo_write_access_mode(hwfifo, ff_buf, n, data_stride);
} else {
// Wrap around case
// Read full words from linear part
uint16_t lin_nitems_bytes = lin_bytes & ~STRIDE_REMAIN_MASK;
tu_hwfifo_write(hwfifo, ff_buf, lin_nitems_bytes);
ff_buf += lin_nitems_bytes;
const uint32_t odd_mask = data_stride - 1;
uint16_t lin_even = lin_bytes & ~odd_mask;
tu_hwfifo_write_access_mode(hwfifo, ff_buf, lin_even, data_stride);
ff_buf += lin_even;
// There could be odd 1 byte (16bit) or 1-3 bytes (32bit) before the wrap-around boundary
const uint8_t rem = lin_bytes & STRIDE_REMAIN_MASK;
if (rem > 0) {
const uint8_t remrem = (uint8_t)tu_min16(wrap_bytes, sizeof(hwfifo_item_t) - rem);
const hwfifo_item_t scatter = (hwfifo_item_t)tu_scatter_read32(ff_buf, rem, f->buffer, remrem);
const uint8_t lin_odd = lin_bytes & odd_mask;
if (lin_odd > 0) {
const uint8_t wrap_odd = (uint8_t)tu_min16(wrap_bytes, data_stride - lin_odd);
const uint32_t scatter = tu_scatter_read32(ff_buf, lin_odd, f->buffer, wrap_odd);
*hwfifo = scatter;
stride_write(hwfifo, &scatter, data_stride);
HWFIFO_ADDR_NEXT(, hwfifo);
wrap_bytes -= remrem;
ff_buf = f->buffer + remrem; // wrap around
wrap_bytes -= wrap_odd;
ff_buf = f->buffer + wrap_odd; // wrap around
} else {
ff_buf = f->buffer; // wrap around to beginning
ff_buf = f->buffer; // wrap around to beginning
}
// Read data wrapped part
if (wrap_bytes > 0) {
tu_hwfifo_write(hwfifo, ff_buf, wrap_bytes);
tu_hwfifo_write_access_mode(hwfifo, ff_buf, wrap_bytes, data_stride);
}
}
} else
@ -349,7 +368,7 @@ static uint16_t correct_read_index(tu_fifo_t *f, uint16_t wr_idx) {
// Works on local copies of w and r
// Must be protected by read mutex since in case of an overflow read pointer gets modified
uint16_t tu_fifo_peek_n_access_mode(tu_fifo_t *f, void *p_buffer, uint16_t n, uint16_t wr_idx, uint16_t rd_idx,
bool stride_mode) {
uint8_t data_stride) {
uint16_t count = tu_ff_overflow_count(f->depth, wr_idx, rd_idx);
if (count == 0) {
return 0; // nothing to peek
@ -366,7 +385,7 @@ uint16_t tu_fifo_peek_n_access_mode(tu_fifo_t *f, void *p_buffer, uint16_t n, ui
}
const uint16_t rd_ptr = idx2ptr(f->depth, rd_idx);
ff_pull_n(f, p_buffer, n, rd_ptr, stride_mode);
ff_pull_n(f, p_buffer, n, rd_ptr, data_stride);
return n;
}
@ -374,17 +393,17 @@ uint16_t tu_fifo_peek_n_access_mode(tu_fifo_t *f, void *p_buffer, uint16_t n, ui
// Read n items without removing it from the FIFO, correct read pointer if overflowed
uint16_t tu_fifo_peek_n(tu_fifo_t *f, void *p_buffer, uint16_t n) {
ff_lock(f->mutex_rd);
const uint16_t ret = tu_fifo_peek_n_access_mode(f, p_buffer, n, f->wr_idx, f->rd_idx, false);
const uint16_t ret = tu_fifo_peek_n_access_mode(f, p_buffer, n, f->wr_idx, f->rd_idx, 0);
ff_unlock(f->mutex_rd);
return ret;
}
// Read n items from fifo with access mode
uint16_t tu_fifo_read_n_access_mode(tu_fifo_t *f, void *buffer, uint16_t n, bool stride_mode) {
uint16_t tu_fifo_read_n_access_mode(tu_fifo_t *f, void *buffer, uint16_t n, uint8_t data_stride) {
ff_lock(f->mutex_rd);
// Peek the data: f->rd_idx might get modified in case of an overflow so we can not use a local variable
n = tu_fifo_peek_n_access_mode(f, buffer, n, f->wr_idx, f->rd_idx, stride_mode);
n = tu_fifo_peek_n_access_mode(f, buffer, n, f->wr_idx, f->rd_idx, data_stride);
f->rd_idx = advance_index(f->depth, f->rd_idx, n);
ff_unlock(f->mutex_rd);
@ -392,7 +411,7 @@ uint16_t tu_fifo_read_n_access_mode(tu_fifo_t *f, void *buffer, uint16_t n, bool
}
// Write n items to fifo with access mode
uint16_t tu_fifo_write_n_access_mode(tu_fifo_t *f, const void *data, uint16_t n, bool stride_mode) {
uint16_t tu_fifo_write_n_access_mode(tu_fifo_t *f, const void *data, uint16_t n, uint8_t data_stride) {
if (n == 0) {
return 0;
}
@ -418,7 +437,7 @@ uint16_t tu_fifo_write_n_access_mode(tu_fifo_t *f, const void *data, uint16_t n,
// function! Since it would end up in a race condition with read functions!
if (n >= f->depth) {
// Only copy last part
if (!stride_mode) {
if (!data_stride) {
buf8 += (n - f->depth);
} else {
// TODO should read from hw fifo to discard data, however reading an odd number could
@ -454,7 +473,7 @@ uint16_t tu_fifo_write_n_access_mode(tu_fifo_t *f, const void *data, uint16_t n,
const uint16_t wr_ptr = idx2ptr(f->depth, wr_idx);
TU_LOG(TU_FIFO_DBG, "actual_n = %u, wr_ptr = %u", n, wr_ptr);
ff_push_n(f, buf8, n, wr_ptr, stride_mode);
ff_push_n(f, buf8, n, wr_ptr, data_stride);
f->wr_idx = advance_index(f->depth, wr_idx, n);
TU_LOG(TU_FIFO_DBG, "\tnew_wr = %u\r\n", f->wr_idx);

View File

@ -193,7 +193,7 @@ void tu_fifo_get_write_info(tu_fifo_t *f, tu_fifo_buffer_info_t *info);
// peek() will correct/re-index read pointer in case of an overflowed fifo to form a full fifo
//--------------------------------------------------------------------+
uint16_t tu_fifo_peek_n_access_mode(tu_fifo_t *f, void *p_buffer, uint16_t n, uint16_t wr_idx, uint16_t rd_idx,
bool stride_mode);
uint8_t data_stride);
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);
@ -201,10 +201,10 @@ uint16_t tu_fifo_peek_n(tu_fifo_t *f, void *p_buffer, uint16_t n);
// Read API
// peek() + advance read index
//--------------------------------------------------------------------+
uint16_t tu_fifo_read_n_access_mode(tu_fifo_t *f, void *buffer, uint16_t n, bool stride_mode);
uint16_t tu_fifo_read_n_access_mode(tu_fifo_t *f, void *buffer, uint16_t n, uint8_t data_stride);
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_mode(f, buffer, n, false);
return tu_fifo_read_n_access_mode(f, buffer, n, 0);
}
// discard first n items from fifo i.e advance read pointer by n with mutex
@ -214,10 +214,10 @@ uint16_t tu_fifo_discard_n(tu_fifo_t *f, uint16_t n);
//--------------------------------------------------------------------+
// Write API
//--------------------------------------------------------------------+
uint16_t tu_fifo_write_n_access_mode(tu_fifo_t *f, const void *data, uint16_t n, bool stride_mode);
uint16_t tu_fifo_write_n_access_mode(tu_fifo_t *f, const void *data, uint16_t n, uint8_t data_stride);
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_mode(f, data, n, false);
return tu_fifo_write_n_access_mode(f, data, n, 0);
}
//--------------------------------------------------------------------+
@ -228,20 +228,30 @@ TU_ATTR_ALWAYS_INLINE static inline uint16_t tu_fifo_write_n(tu_fifo_t *f, const
//--------------------------------------------------------------------+
TU_ATTR_ALWAYS_INLINE static inline uint16_t tu_hwfifo_write_from_fifo(volatile void *hwfifo, tu_fifo_t *f,
uint16_t n) {
return tu_fifo_read_n_access_mode(f, (void *)(uintptr_t)hwfifo, n, true);
return tu_fifo_read_n_access_mode(f, (void *)(uintptr_t)hwfifo, n, CFG_TUSB_FIFO_HWFIFO_DATA_STRIDE);
}
TU_ATTR_ALWAYS_INLINE static inline uint16_t tu_hwfifo_read_to_fifo(const volatile void *hwfifo, tu_fifo_t *f,
uint16_t n) {
return tu_fifo_write_n_access_mode(f, (const void *)(uintptr_t)hwfifo, n, true);
return tu_fifo_write_n_access_mode(f, (const void *)(uintptr_t)hwfifo, n, CFG_TUSB_FIFO_HWFIFO_DATA_STRIDE);
}
#if CFG_TUSB_FIFO_HWFIFO_API
// read from hwfifo to buffer
void tu_hwfifo_read(const volatile void *hwfifo, uint8_t *dest, uint16_t len);
// read from hwfifo to buffer with access mode
void tu_hwfifo_read_access_mode(const volatile void *hwfifo, uint8_t *dest, uint16_t len, uint8_t data_stride);
// write to hwfifo from buffer
void tu_hwfifo_write(volatile void *hwfifo, const uint8_t *src, uint16_t len);
// read from hwfifo to buffer with default data stride
TU_ATTR_ALWAYS_INLINE static inline void tu_hwfifo_read(const volatile void *hwfifo, uint8_t *dest, uint16_t len) {
tu_hwfifo_read_access_mode(hwfifo, dest, len, CFG_TUSB_FIFO_HWFIFO_DATA_STRIDE);
}
// write to hwfifo from buffer with access mode
void tu_hwfifo_write_access_mode(volatile void *hwfifo, const uint8_t *src, uint16_t len, uint8_t data_stride);
// write to hwfifo from buffer with default data stride
TU_ATTR_ALWAYS_INLINE static inline void tu_hwfifo_write(volatile void *hwfifo, const uint8_t *src, uint16_t len) {
tu_hwfifo_write_access_mode(hwfifo, src, len, CFG_TUSB_FIFO_HWFIFO_DATA_STRIDE);
}
#endif
//--------------------------------------------------------------------+

View File

@ -129,7 +129,7 @@
:test:
- _UNITY_TEST_
- CFG_TUD_EDPT_DEDICATED_HWFIFO=1
- CFG_TUSB_FIFO_HWFIFO_DATA_STRIDE=4
- CFG_TUSB_FIFO_HWFIFO_DATA_STRIDE=6
- CFG_TUSB_FIFO_HWFIFO_ADDR_STRIDE=0
:release: []

View File

@ -403,7 +403,7 @@ void test_write_n_fixed_addr_rw32_nowrap(void) {
for (uint8_t n = 1; n <= 8; n++) {
tu_fifo_clear(ff);
uint16_t written = tu_fifo_write_n_access_mode(ff, (const void *)&reg, n, true);
uint16_t written = tu_fifo_write_n_access_mode(ff, (const void *)&reg, n, 4);
TEST_ASSERT_EQUAL(n, written);
TEST_ASSERT_EQUAL(n, tu_fifo_count(ff));
@ -425,7 +425,7 @@ void test_write_n_fixed_addr_rw32_wrapped(void) {
ff->wr_idx = FIFO_SIZE - 3;
ff->rd_idx = FIFO_SIZE - 3;
uint16_t written = tu_fifo_write_n_access_mode(ff, (const void *)&reg, n, true);
uint16_t written = tu_fifo_write_n_access_mode(ff, (const void *)&reg, n, 4);
TEST_ASSERT_EQUAL(n, written);
TEST_ASSERT_EQUAL(n, tu_fifo_count(ff));
@ -445,7 +445,7 @@ void test_read_n_fixed_addr_rw32_nowrap(void) {
tu_fifo_write_n(ff, pattern, 8);
uint32_t reg = 0;
uint16_t read_cnt = tu_fifo_read_n_access_mode(ff, &reg, n, true);
uint16_t read_cnt = tu_fifo_read_n_access_mode(ff, &reg, n, 4);
TEST_ASSERT_EQUAL(n, read_cnt);
TEST_ASSERT_EQUAL(8 - n, tu_fifo_count(ff));
@ -469,7 +469,7 @@ void test_read_n_fixed_addr_rw32_wrapped(void) {
}
uint32_t reg = 0;
uint16_t read_cnt = tu_fifo_read_n_access_mode(ff, &reg, n, true);
uint16_t read_cnt = tu_fifo_read_n_access_mode(ff, &reg, n, 4);
TEST_ASSERT_EQUAL(n, read_cnt);
TEST_ASSERT_EQUAL(0, tu_fifo_count(ff));
@ -477,6 +477,86 @@ void test_read_n_fixed_addr_rw32_wrapped(void) {
}
}
void test_write_n_fixed_addr_rw16_nowrap(void) {
tu_fifo_clear(ff);
volatile uint16_t reg = 0x1122;
uint8_t expected[6] = {0x22, 0x11, 0x22, 0x11, 0x22, 0x11};
for (uint8_t n = 1; n <= 6; n++) {
tu_fifo_clear(ff);
uint16_t written = tu_fifo_write_n_access_mode(ff, (const void *)&reg, n, 2);
TEST_ASSERT_EQUAL(n, written);
TEST_ASSERT_EQUAL(n, tu_fifo_count(ff));
uint8_t out[6] = {0};
tu_fifo_read_n(ff, out, n);
TEST_ASSERT_EQUAL_UINT8_ARRAY(expected, out, n);
}
}
void test_write_n_fixed_addr_rw16_wrapped(void) {
tu_fifo_clear(ff);
volatile uint16_t reg = 0xA1B2;
uint8_t expected[6] = {0xB2, 0xA1, 0xB2, 0xA1, 0xB2, 0xA1};
for (uint8_t n = 1; n <= 6; n++) {
tu_fifo_clear(ff);
// Position the fifo near the end so writes wrap
ff->wr_idx = FIFO_SIZE - 3;
ff->rd_idx = FIFO_SIZE - 3;
uint16_t written = tu_fifo_write_n_access_mode(ff, (const void *)&reg, n, 2);
TEST_ASSERT_EQUAL(n, written);
TEST_ASSERT_EQUAL(n, tu_fifo_count(ff));
uint8_t out[6] = {0};
tu_fifo_read_n(ff, out, n);
TEST_ASSERT_EQUAL_UINT8_ARRAY(expected, out, n);
}
}
void test_read_n_fixed_addr_rw16_nowrap(void) {
uint8_t pattern[6] = {0x10, 0x21, 0x32, 0x43, 0x54, 0x65};
uint16_t reg_expected[6] = {0x0010, 0x2110, 0x0032, 0x4332, 0x0054, 0x6554};
for (uint8_t n = 1; n <= 6; n++) {
tu_fifo_clear(ff);
tu_fifo_write_n(ff, pattern, 6);
uint16_t reg = 0;
uint16_t read_cnt = tu_fifo_read_n_access_mode(ff, &reg, n, 2);
TEST_ASSERT_EQUAL(n, read_cnt);
TEST_ASSERT_EQUAL(6 - n, tu_fifo_count(ff));
TEST_ASSERT_EQUAL_HEX16(reg_expected[n - 1], reg);
}
}
void test_read_n_fixed_addr_rw16_wrapped(void) {
uint8_t pattern[6] = {0xF0, 0xE1, 0xD2, 0xC3, 0xB4, 0xA5};
uint16_t reg_expected[6] = {0x00F0, 0xE1F0, 0x00D2, 0xC3D2, 0x00B4, 0xA5B4};
for (uint8_t n = 1; n <= 6; n++) {
tu_fifo_clear(ff);
ff->rd_idx = FIFO_SIZE - 1;
ff->wr_idx = (uint16_t)(ff->rd_idx + n);
for (uint8_t i = 0; i < n; i++) {
uint8_t idx = (uint8_t)((ff->rd_idx + i) % FIFO_SIZE);
ff->buffer[idx] = pattern[i];
}
uint16_t reg = 0;
uint16_t read_cnt = tu_fifo_read_n_access_mode(ff, &reg, n, 2);
TEST_ASSERT_EQUAL(n, read_cnt);
TEST_ASSERT_EQUAL(0, tu_fifo_count(ff));
TEST_ASSERT_EQUAL_HEX16(reg_expected[n - 1], reg);
}
}
void test_get_read_info_advanced_cases(void) {
tu_fifo_clear(ff);