예제 #1
0
volatile void *flashcdw_memset64(volatile void *dst, uint64_t src, size_t nbytes, bool erase)
{
	// Use aggregated pointers to have several alignments available for a same address.
	UnionCVPtr flash_array_end;
	UnionVPtr dest;
	Union64 source = {0};
	StructCVPtr dest_end;
	UnionCVPtr flash_page_source_end;
	bool incomplete_flash_page_end;
	Union64 flash_dword;
	UnionVPtr tmp;
	unsigned int error_status = 0;
	unsigned int i;

	// Reformat arguments.
	flash_array_end.u8ptr = AVR32_FLASH + flashcdw_get_flash_size();
	dest.u8ptr = dst;
	for (i = (Get_align((uint32_t)dest.u8ptr, sizeof(uint64_t)) - 1) & (sizeof(uint64_t) - 1);
			src; i = (i - 1) & (sizeof(uint64_t) - 1)) {
		source.u8[i] = src;
		src >>= 8;
	}
	dest_end.u8ptr = dest.u8ptr + nbytes;

	// If destination is outside flash, go to next flash page if any.
	if (dest.u8ptr < AVR32_FLASH) {
		dest.u8ptr = AVR32_FLASH;
	} else if (flash_array_end.u8ptr <= dest.u8ptr && dest.u8ptr < AVR32_FLASHCDW_USER_PAGE) {
		dest.u8ptr = AVR32_FLASHCDW_USER_PAGE;
	}

	// If end of destination is outside flash, move it to the end of the previous flash page if any.
	if (dest_end.u8ptr > AVR32_FLASHCDW_USER_PAGE + AVR32_FLASHCDW_USER_PAGE_SIZE) {
		dest_end.u8ptr = AVR32_FLASHCDW_USER_PAGE + AVR32_FLASHCDW_USER_PAGE_SIZE;
	} else if (AVR32_FLASHCDW_USER_PAGE >= dest_end.u8ptr && dest_end.u8ptr > flash_array_end.u8ptr) {
		dest_end.u8ptr = flash_array_end.u8ptr;
	}

	// Align each end of destination pointer with its natural boundary.
	dest_end.u16ptr = (uint16_t *)Align_down((uint32_t)dest_end.u8ptr, sizeof(uint16_t));
	dest_end.u32ptr = (uint32_t *)Align_down((uint32_t)dest_end.u16ptr, sizeof(uint32_t));
	dest_end.u64ptr = (uint64_t *)Align_down((uint32_t)dest_end.u32ptr, sizeof(uint64_t));

	// While end of destination is not reached...
	while (dest.u8ptr < dest_end.u8ptr) {
		// Clear the page buffer in order to prepare data for a flash page write.
		flashcdw_clear_page_buffer();
		error_status |= flashcdw_error_status;

		// Determine where the source data will end in the current flash page.
		flash_page_source_end.u64ptr = (uint64_t *)min((uint32_t)dest_end.u64ptr,
				Align_down((uint32_t)dest.u8ptr, AVR32_FLASHCDW_PAGE_SIZE) + AVR32_FLASHCDW_PAGE_SIZE);

		// Determine if the current destination page has an incomplete end.
		incomplete_flash_page_end = (Align_down((uint32_t)dest.u8ptr, AVR32_FLASHCDW_PAGE_SIZE) >=
				Align_down((uint32_t)dest_end.u8ptr, AVR32_FLASHCDW_PAGE_SIZE));

		// Use a flash double-word buffer to manage unaligned accesses.
		flash_dword.u64 = source.u64;

		// If destination does not point to the beginning of the current flash page...
		if (!Test_align((uint32_t)dest.u8ptr, AVR32_FLASHCDW_PAGE_SIZE)) {
			// Fill the beginning of the page buffer with the current flash page data.
			// This is required by the hardware, even if page erase is not requested,
			// in order to be able to write successfully to erased parts of flash
			// pages that have already been written to.
			for (tmp.u8ptr = (uint8_t *)Align_down((uint32_t)dest.u8ptr, AVR32_FLASHCDW_PAGE_SIZE);
					tmp.u64ptr < (uint64_t *)Align_down((uint32_t)dest.u8ptr, sizeof(uint64_t));
					tmp.u64ptr++) {
				*tmp.u32ptr = *tmp.u32ptr;
				*(tmp.u32ptr+1) = *(tmp.u32ptr+1);
			}

			// If destination is not 64-bit aligned...
			if (!Test_align((uint32_t)dest.u8ptr, sizeof(uint64_t))) {
				// Fill the beginning of the flash double-word buffer with the current
				// flash page data.
				// This is required by the hardware, even if page erase is not
				// requested, in order to be able to write successfully to erased parts
				// of flash pages that have already been written to.
				for (i = 0; i < Get_align((uint32_t)dest.u8ptr, sizeof(uint64_t)); i++) {
					flash_dword.u8[i] = *tmp.u8ptr++;
				}

				// Align the destination pointer with its 64-bit boundary.
				dest.u64ptr = (uint64_t *)Align_down((uint32_t)dest.u8ptr, sizeof(uint64_t));

				// If the current destination double-word is not the last one...
				if (dest.u64ptr < dest_end.u64ptr) {
					// Write the flash double-word buffer to the page buffer and reinitialize it.
					*dest.u32ptr++ = flash_dword.u32[0];
					*dest.u32ptr++ = flash_dword.u32[1];
					flash_dword.u64 = source.u64;
				}
			}
		}

		// Write the source data to the page buffer with 64-bit alignment.
		for (i = flash_page_source_end.u64ptr - dest.u64ptr; i; i--) {
			*dest.u32ptr++ = source.u32[0];
			*dest.u32ptr++ = source.u32[1];
		}

		// If the current destination page has an incomplete end...
		if (incomplete_flash_page_end) {
			// This is required by the hardware, even if page erase is not requested,
			// in order to be able to write successfully to erased parts of flash
			// pages that have already been written to.
			{
				tmp.u8ptr = (volatile uint8_t *)dest_end.u8ptr;

				// If end of destination is not 64-bit aligned...
				if (!Test_align((uint32_t)dest_end.u8ptr, sizeof(uint64_t))) {
					// Fill the end of the flash double-word buffer with the current flash page data.
					for (i = Get_align((uint32_t)dest_end.u8ptr, sizeof(uint64_t)); i < sizeof(uint64_t); i++) {
						flash_dword.u8[i] = *tmp.u8ptr++;
					}

					// Write the flash double-word buffer to the page buffer.
					*dest.u32ptr++ = flash_dword.u32[0];
					*dest.u32ptr++ = flash_dword.u32[1];
				}

				// Fill the end of the page buffer with the current flash page data.
				for (; !Test_align((uint32_t)tmp.u64ptr, AVR32_FLASHCDW_PAGE_SIZE); tmp.u64ptr++) {
					*tmp.u32ptr = *tmp.u32ptr;
					*(tmp.u32ptr+1) = *(tmp.u32ptr+1);
				}
			}
		}

		// If the current flash page is in the flash array...
		if (dest.u8ptr <= AVR32_FLASHCDW_USER_PAGE) {
			// Erase the current page if requested and write it from the page buffer.
			if (erase) {
				flashcdw_erase_page(-1, false);
				error_status |= flashcdw_error_status;
			}
			flashcdw_write_page(-1);
			error_status |= flashcdw_error_status;

			// If the end of the flash array is reached, go to the User page.
			if (dest.u8ptr >= flash_array_end.u8ptr) {
				dest.u8ptr = AVR32_FLASHCDW_USER_PAGE;
			}
		} else {
			// Erase the User page if requested and write it from the page buffer.
			if (erase) {
				flashcdw_erase_user_page(false);
				error_status |= flashcdw_error_status;
			}
			flashcdw_write_user_page();
			error_status |= flashcdw_error_status;
		}
	}

	// Update the FLASHC error status.
	flashcdw_error_status = error_status;

	// Return the initial destination pointer as the standard memset function does.
	return dst;
}
예제 #2
0
파일: usb_drv.c 프로젝트: Sac9665/Libraries
//! host_read_p_rxpacket
//!
//!  This function reads the selected pipe FIFO to the buffer pointed to by
//!  rxbuf, using as few accesses as possible.
//!
//! @param p            Number of the addressed pipe
//! @param rxbuf        Address of buffer to write
//! @param data_length  Number of bytes to read
//! @param prxbuf       NULL or pointer to the buffer address to update
//!
//! @return             Number of read bytes
//!
//! @note The selected pipe FIFO may be read in several steps by calling
//! host_read_p_rxpacket several times.
//!
//! @warning Invoke Host_reset_pipe_fifo_access before this function when at
//! FIFO beginning whether or not the FIFO is to be read in several steps.
//!
//! @warning Do not mix calls to this function with calls to indexed macros.
//!
U32 host_read_p_rxpacket(U8 p, void *rxbuf, U32 data_length, void **prxbuf)
{
  // Use aggregated pointers to have several alignments available for a same address
  UnionCVPtr  p_fifo;
  UnionPtr    rxbuf_cur;
#if (!defined __OPTIMIZE_SIZE__) || !__OPTIMIZE_SIZE__  // Auto-generated when GCC's -Os command option is used
  StructCPtr  rxbuf_end;
#else
  UnionCPtr   rxbuf_end;
#endif  // !__OPTIMIZE_SIZE__

  // Initialize pointers for copy loops and limit the number of bytes to copy
  p_fifo.u8ptr = pep_fifo[p].u8ptr;
  rxbuf_cur.u8ptr = rxbuf;
  rxbuf_end.u8ptr = rxbuf_cur.u8ptr + min(data_length, Host_byte_count(p));
#if (!defined __OPTIMIZE_SIZE__) || !__OPTIMIZE_SIZE__  // Auto-generated when GCC's -Os command option is used
  rxbuf_end.u16ptr = (U16 *)Align_down((U32)rxbuf_end.u8ptr, sizeof(U16));
  rxbuf_end.u32ptr = (U32 *)Align_down((U32)rxbuf_end.u16ptr, sizeof(U32));
  rxbuf_end.u64ptr = (U64 *)Align_down((U32)rxbuf_end.u32ptr, sizeof(U64));

  // If all addresses are aligned the same way with respect to 16-bit boundaries
  if (Get_align((U32)rxbuf_cur.u8ptr, sizeof(U16)) == Get_align((U32)p_fifo.u8ptr, sizeof(U16)))
  {
    // If pointer to reception buffer is not 16-bit aligned
    if (!Test_align((U32)rxbuf_cur.u8ptr, sizeof(U16)))
    {
      // Copy 8-bit data to reach 16-bit alignment
      if (rxbuf_cur.u8ptr < rxbuf_end.u8ptr)
      {
        // 8-bit accesses to FIFO data registers do require pointer post-increment
        *rxbuf_cur.u8ptr++ = *p_fifo.u8ptr++;
      }
    }

    // If all addresses are aligned the same way with respect to 32-bit boundaries
    if (Get_align((U32)rxbuf_cur.u16ptr, sizeof(U32)) == Get_align((U32)p_fifo.u16ptr, sizeof(U32)))
    {
      // If pointer to reception buffer is not 32-bit aligned
      if (!Test_align((U32)rxbuf_cur.u16ptr, sizeof(U32)))
      {
        // Copy 16-bit data to reach 32-bit alignment
        if (rxbuf_cur.u16ptr < rxbuf_end.u16ptr)
        {
          // 16-bit accesses to FIFO data registers do require pointer post-increment
          *rxbuf_cur.u16ptr++ = *p_fifo.u16ptr++;
        }
      }

      // If pointer to reception buffer is not 64-bit aligned
      if (!Test_align((U32)rxbuf_cur.u32ptr, sizeof(U64)))
      {
        // Copy 32-bit data to reach 64-bit alignment
        if (rxbuf_cur.u32ptr < rxbuf_end.u32ptr)
        {
          // 32-bit accesses to FIFO data registers do not require pointer post-increment
          *rxbuf_cur.u32ptr++ = *p_fifo.u32ptr;
        }
      }

      // Copy 64-bit-aligned data
      while (rxbuf_cur.u64ptr < rxbuf_end.u64ptr)
      {
        // 64-bit accesses to FIFO data registers do not require pointer post-increment
        *rxbuf_cur.u64ptr++ = *p_fifo.u64ptr;
      }

      // Copy 32-bit-aligned data
      if (rxbuf_cur.u32ptr < rxbuf_end.u32ptr)
      {
        // 32-bit accesses to FIFO data registers do not require pointer post-increment
        *rxbuf_cur.u32ptr++ = *p_fifo.u32ptr;
      }
    }

    // Copy remaining 16-bit data if some
    while (rxbuf_cur.u16ptr < rxbuf_end.u16ptr)
    {
      // 16-bit accesses to FIFO data registers do require pointer post-increment
      *rxbuf_cur.u16ptr++ = *p_fifo.u16ptr++;
    }
  }

#endif  // !__OPTIMIZE_SIZE__

  // Copy remaining 8-bit data if some
  while (rxbuf_cur.u8ptr < rxbuf_end.u8ptr)
  {
    // 8-bit accesses to FIFO data registers do require pointer post-increment
    *rxbuf_cur.u8ptr++ = *p_fifo.u8ptr++;
  }

  // Save current position in FIFO data register
  pep_fifo[p].u8ptr = (volatile U8 *)p_fifo.u8ptr;

  // Return the updated buffer address and the number of copied bytes
  if (prxbuf) *prxbuf = rxbuf_cur.u8ptr;
  return (rxbuf_cur.u8ptr - (U8 *)rxbuf);
}
volatile void* flashc_memcpy (volatile void* dst, const void* src, size_t nbytes, Bool erase)
{
    // Use aggregated pointers to have several alignments available for a same address.
    UnionCVPtr flash_array_end;
    UnionVPtr dest;
    UnionCPtr source;
    StructCVPtr dest_end;
    UnionCVPtr flash_page_source_end;
    Bool incomplete_flash_page_end;
    Union64 flash_dword;
    Bool flash_dword_pending = FALSE;
    UnionVPtr tmp;
    unsigned int error_status = 0;
    unsigned int i, j;

    // Reformat arguments.
    flash_array_end.u8ptr = AVR32_FLASH + flashc_get_flash_size ();
    dest.u8ptr = dst;
    source.u8ptr = src;
    dest_end.u8ptr = dest.u8ptr + nbytes;

    // If destination is outside flash, go to next flash page if any.
    if (dest.u8ptr < AVR32_FLASH)
    {
        source.u8ptr += AVR32_FLASH - dest.u8ptr;
        dest.u8ptr = AVR32_FLASH;
    }
    else if (flash_array_end.u8ptr <= dest.u8ptr && dest.u8ptr < AVR32_FLASHC_USER_PAGE)
    {
        source.u8ptr += AVR32_FLASHC_USER_PAGE - dest.u8ptr;
        dest.u8ptr = AVR32_FLASHC_USER_PAGE;
    }

    // If end of destination is outside flash, move it to the end of the previous flash page if any.
    if (dest_end.u8ptr > AVR32_FLASHC_USER_PAGE + AVR32_FLASHC_USER_PAGE_SIZE)
    {
        dest_end.u8ptr = AVR32_FLASHC_USER_PAGE + AVR32_FLASHC_USER_PAGE_SIZE;
    }
    else if (AVR32_FLASHC_USER_PAGE >= dest_end.u8ptr && dest_end.u8ptr > flash_array_end.u8ptr)
    {
        dest_end.u8ptr = flash_array_end.u8ptr;
    }

    // Align each end of destination pointer with its natural boundary.
    dest_end.u16ptr = (U16 *) Align_down ((U32) dest_end.u8ptr, sizeof (U16));
    dest_end.u32ptr = (U32 *) Align_down ((U32) dest_end.u16ptr, sizeof (U32));
    dest_end.u64ptr = (U64 *) Align_down ((U32) dest_end.u32ptr, sizeof (U64));

    // While end of destination is not reached...
    while (dest.u8ptr < dest_end.u8ptr)
    {
        // Clear the page buffer in order to prepare data for a flash page write.
        flashc_clear_page_buffer ();
        error_status |= flashc_error_status;

        // Determine where the source data will end in the current flash page.
        flash_page_source_end.u64ptr =
            (U64 *) min ((U32) dest_end.u64ptr, Align_down ((U32) dest.u8ptr, AVR32_FLASHC_PAGE_SIZE) + AVR32_FLASHC_PAGE_SIZE);

        // Determine if the current destination page has an incomplete end.
        incomplete_flash_page_end = (Align_down ((U32) dest.u8ptr, AVR32_FLASHC_PAGE_SIZE) >=
                                     Align_down ((U32) dest_end.u8ptr, AVR32_FLASHC_PAGE_SIZE));

        // If destination does not point to the beginning of the current flash page...
        if (!Test_align ((U32) dest.u8ptr, AVR32_FLASHC_PAGE_SIZE))
        {
            // Fill the beginning of the page buffer with the current flash page data.
            // This is required by the hardware, even if page erase is not requested,
            // in order to be able to write successfully to erased parts of flash
            // pages that have already been written to.
            for (tmp.u8ptr = (U8 *) Align_down ((U32) dest.u8ptr, AVR32_FLASHC_PAGE_SIZE);
                 tmp.u64ptr < (U64 *) Align_down ((U32) dest.u8ptr, sizeof (U64)); tmp.u64ptr++)
                *tmp.u64ptr = *tmp.u64ptr;

            // If destination is not 64-bit aligned...
            if (!Test_align ((U32) dest.u8ptr, sizeof (U64)))
            {
                // Fill the beginning of the flash double-word buffer with the current
                // flash page data.
                // This is required by the hardware, even if page erase is not
                // requested, in order to be able to write successfully to erased parts
                // of flash pages that have already been written to.
                for (i = 0; i < Get_align ((U32) dest.u8ptr, sizeof (U64)); i++)
                    flash_dword.u8[i] = *tmp.u8ptr++;

                // Fill the end of the flash double-word buffer with the source data.
                for (; i < sizeof (U64); i++)
                    flash_dword.u8[i] = *source.u8ptr++;

                // Align the destination pointer with its 64-bit boundary.
                dest.u64ptr = (U64 *) Align_down ((U32) dest.u8ptr, sizeof (U64));

                // If the current destination double-word is not the last one...
                if (dest.u64ptr < dest_end.u64ptr)
                {
                    // Write the flash double-word buffer to the page buffer.
                    *dest.u64ptr++ = flash_dword.u64;
                }
                // If the current destination double-word is the last one, the flash
                // double-word buffer must be kept for later.
                else
                    flash_dword_pending = TRUE;
            }
        }

        // Read the source data with the maximal possible alignment and write it to
        // the page buffer with 64-bit alignment.
        switch (Get_align ((U32) source.u8ptr, sizeof (U32)))
        {
            case 0:
                for (i = flash_page_source_end.u64ptr - dest.u64ptr; i; i--)
                    *dest.u64ptr++ = *source.u64ptr++;
                break;

            case sizeof (U16):
                for (i = flash_page_source_end.u64ptr - dest.u64ptr; i; i--)
                {
                    for (j = 0; j < sizeof (U64) / sizeof (U16); j++)
                        flash_dword.u16[j] = *source.u16ptr++;
                    *dest.u64ptr++ = flash_dword.u64;
                }
                break;

            default:
                for (i = flash_page_source_end.u64ptr - dest.u64ptr; i; i--)
                {
                    for (j = 0; j < sizeof (U64); j++)
                        flash_dword.u8[j] = *source.u8ptr++;
                    *dest.u64ptr++ = flash_dword.u64;
                }
        }

        // If the current destination page has an incomplete end...
        if (incomplete_flash_page_end)
        {
            // If the flash double-word buffer is in use, do not initialize it.
            if (flash_dword_pending)
                i = Get_align ((U32) dest_end.u8ptr, sizeof (U64));
            // If the flash double-word buffer is free...
            else
            {
                // Fill the beginning of the flash double-word buffer with the source data.
                for (i = 0; i < Get_align ((U32) dest_end.u8ptr, sizeof (U64)); i++)
                    flash_dword.u8[i] = *source.u8ptr++;
            }

            // This is required by the hardware, even if page erase is not requested,
            // in order to be able to write successfully to erased parts of flash
            // pages that have already been written to.
            {
                tmp.u8ptr = (volatile U8 *) dest_end.u8ptr;

                // If end of destination is not 64-bit aligned...
                if (!Test_align ((U32) dest_end.u8ptr, sizeof (U64)))
                {
                    // Fill the end of the flash double-word buffer with the current flash page data.
                    for (; i < sizeof (U64); i++)
                        flash_dword.u8[i] = *tmp.u8ptr++;

                    // Write the flash double-word buffer to the page buffer.
                    *dest.u64ptr++ = flash_dword.u64;
                }

                // Fill the end of the page buffer with the current flash page data.
                for (; !Test_align ((U32) tmp.u64ptr, AVR32_FLASHC_PAGE_SIZE); tmp.u64ptr++)
                    *tmp.u64ptr = *tmp.u64ptr;
            }
        }

        // If the current flash page is in the flash array...
        if (dest.u8ptr <= AVR32_FLASHC_USER_PAGE)
        {
            // Erase the current page if requested and write it from the page buffer.
            if (erase)
            {
                flashc_erase_page (-1, FALSE);
                error_status |= flashc_error_status;
            }
            flashc_write_page (-1);
            error_status |= flashc_error_status;

            // If the end of the flash array is reached, go to the User page.
            if (dest.u8ptr >= flash_array_end.u8ptr)
            {
                source.u8ptr += AVR32_FLASHC_USER_PAGE - dest.u8ptr;
                dest.u8ptr = AVR32_FLASHC_USER_PAGE;
            }
        }
        // If the current flash page is the User page...
        else
        {
            // Erase the User page if requested and write it from the page buffer.
            if (erase)
            {
                flashc_erase_user_page (FALSE);
                error_status |= flashc_error_status;
            }
            flashc_write_user_page ();
            error_status |= flashc_error_status;
        }
    }

    // Update the FLASHC error status.
    flashc_error_status = error_status;

    // Return the initial destination pointer as the standard memcpy function does.
    return dst;
}
예제 #4
0
//! usb_write_ep_txpacket
//!
//!  This function writes the buffer pointed to by txbuf to the selected
//!  endpoint FIFO, using as few accesses as possible.
//!
//! @param ep           Number of the addressed endpoint
//! @param txbuf        Address of buffer to read
//! @param data_length  Number of bytes to write
//! @param ptxbuf       NULL or pointer to the buffer address to update
//!
//! @return             Number of non-written bytes
//!
//! @note The selected endpoint FIFO may be written in several steps by calling
//! usb_write_ep_txpacket several times.
//!
//! @warning Invoke Usb_reset_endpoint_fifo_access before this function when at
//! FIFO beginning whether or not the FIFO is to be written in several steps.
//!
//! @warning Do not mix calls to this function with calls to indexed macros.
//!
U32 usb_write_ep_txpacket(U8 ep, const void *txbuf, U32 data_length, const void **ptxbuf)
{
  // Use aggregated pointers to have several alignments available for a same address
  UnionVPtr   ep_fifo;
  UnionCPtr   txbuf_cur;
#if (!defined __OPTIMIZE_SIZE__) || !__OPTIMIZE_SIZE__  // Auto-generated when GCC's -Os command option is used
  StructCPtr  txbuf_end;
#else
  UnionCPtr   txbuf_end;
#endif  // !__OPTIMIZE_SIZE__

  // Initialize pointers for copy loops and limit the number of bytes to copy
  ep_fifo.u8ptr = pep_fifo[ep].u8ptr;
  txbuf_cur.u8ptr = txbuf;
  txbuf_end.u8ptr = txbuf_cur.u8ptr +
                    min(data_length, Usb_get_endpoint_size(ep) - Usb_byte_count(ep));
#if (!defined __OPTIMIZE_SIZE__) || !__OPTIMIZE_SIZE__  // Auto-generated when GCC's -Os command option is used
  txbuf_end.u16ptr = (U16 *)Align_down((U32)txbuf_end.u8ptr, sizeof(U16));
  txbuf_end.u32ptr = (U32 *)Align_down((U32)txbuf_end.u16ptr, sizeof(U32));
  txbuf_end.u64ptr = (U64 *)Align_down((U32)txbuf_end.u32ptr, sizeof(U64));

  // If all addresses are aligned the same way with respect to 16-bit boundaries
  if (Get_align((U32)txbuf_cur.u8ptr, sizeof(U16)) == Get_align((U32)ep_fifo.u8ptr, sizeof(U16)))
  {
    // If pointer to transmission buffer is not 16-bit aligned
    if (!Test_align((U32)txbuf_cur.u8ptr, sizeof(U16)))
    {
      // Copy 8-bit data to reach 16-bit alignment
      if (txbuf_cur.u8ptr < txbuf_end.u8ptr)
      {
        // 8-bit accesses to FIFO data registers do require pointer post-increment
        *ep_fifo.u8ptr++ = *txbuf_cur.u8ptr++;
      }
    }

    // If all addresses are aligned the same way with respect to 32-bit boundaries
    if (Get_align((U32)txbuf_cur.u16ptr, sizeof(U32)) == Get_align((U32)ep_fifo.u16ptr, sizeof(U32)))
    {
      // If pointer to transmission buffer is not 32-bit aligned
      if (!Test_align((U32)txbuf_cur.u16ptr, sizeof(U32)))
      {
        // Copy 16-bit data to reach 32-bit alignment
        if (txbuf_cur.u16ptr < txbuf_end.u16ptr)
        {
          // 16-bit accesses to FIFO data registers do require pointer post-increment
          *ep_fifo.u16ptr++ = *txbuf_cur.u16ptr++;
        }
      }

      // If pointer to transmission buffer is not 64-bit aligned
      if (!Test_align((U32)txbuf_cur.u32ptr, sizeof(U64)))
      {
        // Copy 32-bit data to reach 64-bit alignment
        if (txbuf_cur.u32ptr < txbuf_end.u32ptr)
        {
          // 32-bit accesses to FIFO data registers do not require pointer post-increment
          *ep_fifo.u32ptr = *txbuf_cur.u32ptr++;
        }
      }

      // Copy 64-bit-aligned data
      while (txbuf_cur.u64ptr < txbuf_end.u64ptr)
      {
        // 64-bit accesses to FIFO data registers do not require pointer post-increment
        *ep_fifo.u64ptr = *txbuf_cur.u64ptr++;
      }

      // Copy 32-bit-aligned data
      if (txbuf_cur.u32ptr < txbuf_end.u32ptr)
      {
        // 32-bit accesses to FIFO data registers do not require pointer post-increment
        *ep_fifo.u32ptr = *txbuf_cur.u32ptr++;
      }
    }

    // Copy remaining 16-bit data if some
    while (txbuf_cur.u16ptr < txbuf_end.u16ptr)
    {
      // 16-bit accesses to FIFO data registers do require pointer post-increment
      *ep_fifo.u16ptr++ = *txbuf_cur.u16ptr++;
    }
  }

#endif  // !__OPTIMIZE_SIZE__

  // Copy remaining 8-bit data if some
  while (txbuf_cur.u8ptr < txbuf_end.u8ptr)
  {
    // 8-bit accesses to FIFO data registers do require pointer post-increment
    *ep_fifo.u8ptr++ = *txbuf_cur.u8ptr++;
  }

  // Save current position in FIFO data register
  pep_fifo[ep].u8ptr = ep_fifo.u8ptr;

  // Return the updated buffer address and the number of non-copied bytes
  if (ptxbuf) *ptxbuf = txbuf_cur.u8ptr;
  return data_length - (txbuf_cur.u8ptr - (U8 *)txbuf);
}