240 likes | 422 Views
Conception logicielle et matérielle d’un décodeur MP3 sur la plate-forme NIOS-Stratix. Steven Derrien Équipe R2D2. Contexte. Module CSE en DIIC 2 (TP) Conception de circuits en VHDL Approche mixte logiciel-matériel Utilisation d’une plate-forme reconfigurable TP « décodeur MP3 »
E N D
Conception logicielle et matérielle d’un décodeur MP3 sur la plate-forme NIOS-Stratix Steven Derrien Équipe R2D2
Contexte • Module CSE en DIIC 2 (TP) • Conception de circuits en VHDL • Approche mixte logiciel-matériel • Utilisation d’une plate-forme reconfigurable • TP « décodeur MP3 » • Exploiter les possibilités de la plate-forme NIOS • Utiliser des méthodologies de conception • Travailler sur une « véritable » application • Objectif du mini-projet • Obtenir une implémentation « temps-réel » de référence, qui sera utilisé pour les TP.
Travail déjà effectué • Portage du code « C » référence • Passage des calculs flottants en entiers • Restructuration du code • Portage sur le NIOS (pas de SGF) • Mise en œuvre sur un OS temps-réel • Utilisation de micro-OS II. • Découpage de l’application en tâches • Communications par files d’attente • Version à 4 tâches fonctionnelle
Chaîne de décodage MP3 Tâche 1 Tâche 2 Tâche 3 Tâche 4
Résultats • Implémentation 5x trop lente pour du temps-réel. • Deux tâches utilisent 80% des ressources CPU • Accélération matérielle de IMDCT et SubBand
Architecture visée Co-processeur (VHDL) FIFO Tâche 2 IMDCT Nios CPU + OS temps-réel FIFO Tâche 1 Co-processeur (VHDL) FIFO Tâche 4 SubBand Tâche 3 FIFO CNA FIFO Bus Avalon
Travail à réaliser • Filtre IMDCT à finaliser • Mise au point de bancs de tests • Intégration du composant dans l’appli • Validation du fonctionnement • Filtre SubBand à réaliser • Conception VHDL de l’accélérateur • Définition de l’interface soft-hard • Intégration du composant dans l’appli • Validation du fonctionnement
Méthodologie de conception en VHDL • Décomposition UT/UC • Définition d’un squelette d’UT • Mémoires, registres • Opérateurs (*,+) • Interconnexions • Dérivation du contrôleur • Associer à chaque calcul un instant d’exécution et une ressource de traitement. • Plusieurs opération en parallèle • Respects des dépendances de données • Vérifier l’absence de conflits • En déduire l’automate de contrôle.
Définition de l’interface On utilisera une interface à base de FIFOs matérielles, qui seront directement connectées au bus Avalon. Le contrôle de flux étant géré par le processeur NIOS • 1 adx = 0 • 2 while(1) { • 3 X[adx]=read_fifo(); • 4 y=0;i=0; • 5 adx=(adx++) % 8; • 6 while(i<8) { • y=y+X[adx]*C[i]; • adx=(adx++) % 8; • i++; • } • write_fifo(y); • 11 };
Choix des ressources de mémorisation On alloue un registre pour chaque variable, et on utilise des mémoire (RAM ou ROM) pour stocker les tableaux. • 1 adx = 0 • 2 while(1) { • 3 X[adx]=read_fifo(); • 4 y=0;i=0; • 5 adx=(adx++) % 8; • 6 while(i<8) { • y=y+X[adx]*C[i]; • adx=(adx++) % 8; • i++; • } • write_fifo(y); • 11 };
Choix des ressources de traitement On alloue ensuite des unités fonctionnelles à notre chemin de données en fonction des opérations présentes dans le bloc de traitement à accélérer. La duplication de certaines unités de traitement permet parfois de tirer parti d’un parallélisme au niveau instruction. • 1 adx = 0 • 2 while(1) { • 3 X[adx]=read_fifo(); • 4 y=0;i=0; • 5 adx=(adx++) % 8; • 6 while(i<8) { • y=y+X[adx]*C[i]; • adx=(adx++) % 8; • i++; • } • write_fifo(y); • 11 };
Signaux de contrôles de l’UT On dispose alors d’une représentation presque complète de l’unité de traitement à laquelle il faut ajouter les signaux de contrôles. • 1 adx = 0 • 2 while(1) { • 3 X[adx]=read_fifo(); • 4 y=0;i=0; • 5 adx=(adx++) % 8; • 6 while(i<8) { • y=y+X[adx]*C[i]; • adx=(adx++) % 8; • i++; • } • write_fifo(y); • 11 };
Allocation et ordonnancement • On effectue ensuite l’allocation et l’ordonnancement des calculs : • À chaque opération, on associe un opérateur matériel, et un instant d’exécution. • 1 adx = 0 • 2 while(1) { • 3 X[adx]=read_fifo(); • 4 y=0;i=0; • 5 adx=(adx++) % 8; • 6 while(i<8) { • y=y+X[adx]*C[i]; • adx=(adx++) % 8; • i++; • } • write_fifo(y); • 11 };
Dérivation du contrôleur Une fois l’allocation et l’ordonnancement effectués, on peut dériver l’automate de contrôle qui commandera l’unité de traitement.
Exercices • Ordonnancement pour des opérateurs pipelinés • Latence Add =1 cycle, Mul=2 cycles • Reprendre l’exercice pour un filtre symétrique: • 1 adx = 0 • 2 while(1) { • 3 X[adx]=read_fifo(); • 4 y=0;i=0; • 5 adx=(adx++) % 4; • 6 while(i<4) { • y=y+(X[adx]+X[7-adx])*C[i]; • adx=(adx++) % 4; • i++; • } • write_fifo(y); • 11 };
Méthodologie • Traces d’exécution pour un fichier MP3 • Testbench C fonctionnant sur traces • Architecture VHDL • TestBench pour chaque bloc important • Testbench VHDL • Simulation des E/S bus, et utilisation des traces • TestBench C « in situ » intégrant le co-processeur • Mise en œuvre de l’interface logicielle • Mesure de performances (speed-up) • Intégration à l’application
Exercice 1 • Reprendre l’exemple SlavePeriph_fifo.vhd • L’intégrer à un système NIOS. • Écrire un programme C de test pour le NIOS. • Valider le fonctionnement de l’ensemble.
Exercice 2 • Se baser sur le code de IMDCT pour proposer un squelette d’unité de traitement avec les caractéristiques suivantes : • Double multiplieur-accumulateur pipeliné • ROM à double port pour iCOS[] et iWin[] • ROM et RAM synchrones • Réfléchir à un ordonnancement. • Exploiter la présence de 2 MAC • Attention aux délais : • Mémoire synchrone • MAC pipeliné
Code IMDCT • while(1) { • block_type=get_fifo(); • for(i= 0;i<36;i++)iin[i]=get_fifo(); • for(p= 0;p<9;p++){ • isum = 0; isum2= 0; • addr_a = 19+(p<<1); • addr_b = 55+(p<<1); • step_a = ((p<<2)+38); • step_b = ((p<<2)+110); • if (step_b>=144) step_b -= 144; • for(m=0;m<18;m++) { • isum += iin[m]*iCOS[addr_a]; • isum2 += iin[m]*iCOS[addr_b]; • addr_a = addr_a + step_a; • if (addr_a>=144) addr_a -= 144; • addr_b = addr_b + step_b; • if (addr_b>=144) addr_b -= 144; • } • iout[p] = isum*iwin[block_type][p]; • iout[17-p] = -isum*iwin[block_type][17-p]; • iout[18+p] = isum2*iwin[block_type][18+p]; • iout[35-p] = isum2*iwin[block_type][35-p]; • } • for(i= 0;i<36;i++) put_fifo(iout[i]); • }
Exercice 3 • Se baser sur le code de SUBBAND pour compléter l’unité de traitement : • Réfléchir à un ordonnancement. • Dériver le contrôleur
Code SubBand while(1) { bufOffset[channel] = (bufOffset[channel] - 64) & 0x3ff; ibufferPtr = &(ibuf[channel][0]); // @ du début de buffer for (k=0; k<32; k++) bandPtr[k] = get_fifo(); for (i=0; i<64; i++) { isum = 0; for (k=0; k<32; k++) { isum += bandPtr[k]*filterInt[i][k]; } addr_ibuf = i+bufOffset[channel]; ibuffferPtr[addr_ibuf] = isum; } for (k=0; k<32; j++) { isum = 0; for (i=0; i<16; i++) { addr_iwin = k + (i<<5); addr_ibuf = k + ((i<<5) + (((i+1)>>1)<<6)) + bufOffset[channel]) & 0x3FF; isum += iwindow[addr_iwin]*ibuffferPtr[addr_ibuf]; } put_fifo(isum); } channel= 1-channel; }