In prima parte a laboratorului am studiat modalitatea de baza de folosire a DMA pentru transfer de date intre memoria locala a SPU si spatiul principal de stocare. Din cauza ca memoria locala este limitata, multe aplicatii vor avea nevoie sa transfere, pe rand, mai multe secvente de date catre SPE, spre procesare. Pentru acest scenariu se pot folosi mai multe metode: liste DMA sau double-buffering (studiat in laboratorul urmator).
O lista DMA este o secventa de elemente de transferat, ce specifica o serie de transferuri DMA intre o singura zona din memoria locala si mai multe zone (posibil discontinue) din spatiul principal de stocare. Se pot construi astfel functii de tip scatter-gather intre memoria locala si spatiul principal de stocare. Toate transferurile initiate pe baza aceleiasi liste au acelasi tag id si folosesc comenzi de acelasi tip (getl, putl etc.). Lista DMA se stocheaza in memoria locala a aceluiasi SPE.
Fiecare element dintr-o lista DMA contine 3 parametri:
typedef struct mfc_list_element { uint64_t notify : 1; // optional stall-and-notify flag uint64_t reserved : 16; // the name speaks for itself uint64_t size : 15; // transfer size in bytes uint64_t eal : 32; // lower 32-bits of an EA in main storage } mfc_list_element_t;
Dupa ce lista este stocata in memoria locala a SPE, se apeleaza comenzi de transfer specifice (cele cu sufix “l”):
mfc_getl
implementeaza comanda getlmfc_putl
implementeaza comanda putl.Aceste functii sunt non-blocante, iar SPU poate continua executarea programului, economisind timp pretios. Insa, desigur, daca ele nu au loc in MFC SPU command queue, vor avea caracter blocant pana la obtinerea unui slot.
Comenzile de transfer de tip lista folosesc parametri similari comenzilor de transfer obisnuite, cu mici mentiuni (vezi exemplul de mai jos):
mfc_getl
este implementat astfel (mfc_putl e implementat similar):
spu_mfcdma64(ls,mfc_ea2h(ea),(unsigned int)(list), list_size, tag, ((tid<<24)|(rid<<16)|MFC_GETL_CMD))
Exemplu transfer lista DMA
mfc_list_element_t list[16] __attribute__((aligned(8))); // Transfer possibly more than 16 KB with one transfer void large_transfer( void *LS, unsigned long long EA, unsigned int nbytes ) { unsigned int i = 0; unsigned int tagid = 0; unsigned int listsize; unsigned int sz; unsigned int ealow = mfc_ea2l(EA); while( nbytes > 0 ) { sz = (nbytes < 16384) ? nbytes : 16384; list[i].size = sz; list[i].eal = ealow; nbytes -= sz; ealow += sz; i++; } listsize = i * sizeof(mfc_list_item_t); mfc_getl(LS, EA, list, listsize, tagid, 0, 0); mfc_write_tag_mask(1 << tagid); mfc_read_tag_status_any(); }