For the parts that do not have dedicated USB memory, the normal system memory is used.
The start of that buffer must be aligned on a 512 byte boundary.
The buffer as a whole must not cross the high/low memory split.
Some parts don't care about the split, many do.
Use the linker to place it in a specific section at a specific location if needed.
Example for GCC:
#ifndef ENDPOINTS_USED
#define ENDPOINTS_USED (3U) /* Including the Control Endpoint */
#endif
#if( ENDPOINTS_USED > 16U )
#error More than 16 End Points is not allowed.
#endif
#define BDT_PER_EP (4U) /* 4 = (TX Odd/Even) + (RX Odd/Even) */
/*
* Every endpoint direction requires two 8-byte Buffer Descriptor (BD)
* entries. This part is Little Endian.
*/
typedef struct BDT
{
uint8_t bdctl_u8; /* Buffer Descriptor Control BD[ 0: 7] */
uint8_t rsvd_u8; /* Reserved: BD[ 8:15] */
uint16_t byte_count_u16; /* Byte Count. Only ten bits are used for the count. BD[16:32] */
uint8_t *addr_ptr_u8; /* Address */
} BDT;
/*
* There are:
* * 16 bidirectionnal end points -> 16 Rx, 16 Tx end points,
* * and there are ODD and EVEN buffer, for ping/ponging, -> 64 bdt's.
*/
static BDT bdt[( ENDPOINTS_USED * BDT_PER_EP ) ] __attribute__( ( __aligned__( 512U ) ) );