• Non ci sono risultati.

Capitolo 4

N/A
N/A
Protected

Academic year: 2021

Condividi "Capitolo 4"

Copied!
62
0
0

Testo completo

(1)

Capitolo 4

[1], [5], [6], [7], [8], [11], [12], [15]

Il codice

La parte maggiormente rilevante di questo progetto di tesi è stata sicuramente la realizzazione del codice da caricare sulla macchina per far compiere alla scheda descritta nel capitolo precedente la funzione di registratore/riproduttore audio con supporto flash card.

Come già detto gran parte del codice venne scritto durante la fase di debug della scheda, non potendo verificare in altro modo il corretto funzionamento della macchina se non tramite opportune parti di codice scritte apposta allo scopo. La maggior parte delle volte poi, una volta rilevato il corretto funzionamento dell’hardware, il codice scritto risultava valido per poter essere utilizzato in seguito, avendo già dato prova di corretto funzionamento durante la fase di test.

È importante notare che la maggior parte del codice è stata scritta in assembler, tranne la parte relativa all’inizializzazione del codec e poco altro. Questo perché l’assembler ha due vantaggi rispetto alla programmazione tramite linguaggio C:

1. Permette, essendo più a basso livello, una maggior “visibilità” relativamente al meccanismo di esecuzione delle istruzioni stesse da parte delle strutture interne del DSP interessate, consentendo un “debug” più “puntuale” del codice e delle parti hardware del sistema dipendenti dalle porzioni di codice scritto. In altre parole un codice a “basso livello” consente una miglior gestione del singolo bit e quindi anche dei segnali hardware che possono essere “testati” in moltissimi

(2)

punti del codice, sia per verificare lo stato attuale della flash card, che per gestire i comandi dall’esterno, ecc.

2. Si decise di scrivere il codice già in assembler in modo da avere un codice già ottimizzato ed efficiente, visto che inizialmente non erano noti i tempi di risposta della flash card e non si sapeva se strada facendo si sarebbe potuto incorrere in problemi tipo codice troppo pesante computazionalmente da far sì che le procedure sforassero il periodo di campionamento, dovendo necessariamente essere, quando si lavora in tempo reale:

C N i i T P

= 1

Pi → è il tempo necessario a compiere la procedura i.

N → è il numero di procedure che si devono compiere tra l’acquisizione di un dato e del successivo (ovvero le procedure scritte nel while( ) del codice).

TC → è il periodo di campionamento.

Quindi si decise di scrivere il codice già in assembler, “prevenendo” la possibilità che tempi di esecuzione troppo lunghi di un codice C non ottimizzato potessero rendere critico il sistema.

La gestione di una scheda così complessa come quella utilizzata richiede una lunga fase (che prende la maggior parte del codice) di programmazione e inizializzazione dei componenti che sono al suo interno, DSP compreso. Dopodiché si deve effettuare un test sui comandi esterni dai quali si decide se si vuole registrare (ovvero scrivere) oppure riprodurre (ovvero leggere). Da questo punto si prosegue l’esecuzione del codice in uno dei due blocchi distinti: quello relativo alla scrittura se volevo registrare e quello relativo alla lettura se volevo riprodurre. I due blocchi sono completamente separati fino

(3)

Registro Riproduco

Figura 1: Le operazioni generali

Quindi si vede che a livello generale il codice è strutturato in modo molto semplice. Nei blocchi riguardanti la registrazione e la riproduzione in realtà si hanno dei loop, ovvero dopo aver trasferito un pacchetto di dati (settore di 512 B che è la quantità minima di dati che si può trasferire ogni volta) il ciclo di istruzioni fatto per trasferire un settore continua fino a esaurimento flash. Se si è in registrazione ci si arresta quando sono stati scritti tutti i settori della flash; in realtà il programma continua a girare e va in polling sui comandi di ingresso per essere pronto ad iniziare una nuova registrazione qualora questa venga comandata dai tasti di ingresso. Se invece si è in riproduzione una volta letti tutti i dati presenti nella flash card la lettura può ricominciare in automatico dall’inizio, per mandare in riproduzione sempre lo stesso messaggio, oppure si può arrestare. Questo si decide in base allo stato di un segnale di status che corrisponde all’opzione Repeat. START Dichiarazione variabili & Inizializzazione hardware Registro o riproduco? Riproduzione Registrazione

(4)

Il passo successivo è quello di andare a vedere più dettagliatamente le sezioni di codice sopra descritte. Si tratteranno quindi, nell’ordine, le seguente parti:

• Dichiarazione variabili e inizializzazione dell’hardware

• Fase di registrazione, ovvero scrittura

• Fase di riproduzione, ovvero lettura

La parte relativa alla scelta tra registrazione e riproduzione fa parte della più ampia sezione dedicata alla gestione dei comandi di ingresso, che sarà oggetto del prossimo capitolo.

Verrà trattata anche la parte relativa alla dichiarazione delle variabili; del resto è più interessante vedere alcune scelte di programmazione dell’hardware che determinano le modalità di funzionamento del sistema.

4.1 Inizializzazione dell’hardware

Andando a vedere più nel dettaglio questa parte iniziale si ha il seguente diagramma di flusso più approfondito:

Definizione funzioni programmazione codec

Dichiarazioni - Ata drive register address - Constant declaration - Variables declaration

(5)

Internal clock initialization (A)

Inizializzazione registri CPU del DSP

Inizializzazione porte seriali del DSP

Inizializzazione Codec UDA1345TS

Inizializzazione Flash card “flash_card_init” - Reset

- Settaggio “Configuration Option Register”

Inizializzazione SRAM

- Inzializzazione Sram Buffer e Flash Pointer - Variabili per accesso a 8 o 16 b

- Flags vari

Internal clock initialization (B)

(6)

Vediamo quindi le singole parti:

4.1.1 Definizione funzioni programmazione codec

In questa sezione vengono definite delle funzioni che saranno usate poi nella fase di programmazione del codec. Tali funzioni si occupano di inviare al codec, con la giusta temporizzazione, i segnali che servono a gestire il protocollo di programmazione. Una delle varie funzioni utilizzate a tale scopo è la “SendOneToUDA”, che serve a mandare un uno logico sul piedino MP4 del codec. Nel paragrafo dedicato alla programmazione del codec si vedrà questo a cosa porta. La funzione presa in esame è la seguente:

void SendOneToUDA(void) {

asm(" ld Status_574,a");

asm(" or #004h, a"); // DATO = 1

asm(" and #0fdh, a"); // Clock = 0

asm(" stl a, Status_574");

asm(" portw Status_574, #0000h");

asm(" rpt #100");

asm(" nop ");

asm(" or #002h, a"); // Clock = 1

asm(" stl a, Status_574");

asm(" portw Status_574, #0000h");

asm(" rpt #100");

asm(" nop "); }

mentre i collegamenti del registro 74LV574 con il resto del circuito sono mostrati nella figura 2 della pagina seguente.

Si vede infatti che i segnali per la configurazione vengono mandati al codec tramite il registro di otto Flip Flop D-Edge Triggered 74LV574, con il quale si mantengono i segnali per il mondo esterno. Il piedino MP4 del codec è collegato all’uscita 3Q di tale registro che corrisponde all’ingresso 3D, al quale fa capo il segnale D2 appartenente al bus dati.

(7)

Figura 2: Registro di otto Flip Flop D-Edge Triggered 74LV574

Quindi volendo mandare un uno su MP4 deve essere corretto soltanto il terzo bit della variabile Status_574, e tale operazione viene fatta facendo l’OR di tale byte con 04h. Si usa in questo modo perché con l’OR si modifica solo il bit voluto e non tutto il resto della configurazione di Status_574.

Poi, sempre per esigenze di temporizzazione, il piedino relativo ad MP3, che corrisponde al piedino D1 del bus dati, viene mandato a zero tramite un AND di Status_574 con FDh; anche in questo caso si usa l’AND per azzerare il bit voluto lasciando stare il resto. L’invio del dato così costruito al registro 574 è immediato perché essendo questo clockato dall’OR tra /IOS e /RW (entrambe attivi bassi) basta fare un processo di scrittura nello spazio di I/O che subito questi due segnali si attivano e il dato presente sul bus D[0..7] viene campionato dal registro. Il comando utilizzato è quindi:

asm(" portw Status_574, #0000h");

(8)

Syntax PORTW Smem, PA

Description This instruction writes a 16 b value from the specified data-memory

location Smem to an external I/O port PA. The IS signal goes low to indicate an I/O access, and the IOSTRB and READY timings are the same as for an external data memory read.

Sempre dai manuali della Texas Instruments per tale DSP la temporizzazione per un accesso alla memoria esterna risulta essere:

Figura 3: Temporizzazione per un accesso a una memoria esterna

Il tempo nel quale l’/IOSTRB va basso (tempo nel quale anche l’R/W risulta basso di sicuro, come si vede nella temporizzazione) è pari, nel caso peggiore (cioè nel caso in cui valga il valore minore possibile) alla seguente espressione:

(9)

essendo td(CLKHISTRBL) =5ns (valore massimo)

ed essendo td(CLKHISTRBH) =−4ns (valore minimo)

come si vede dai data-sheet del DSP. Inoltre va notato che a questo livello il periodo del clock vale ancora 100 ns in quanto la sezione appena superata dell’Internal clock initialization (A) imposta ad un valore unitario il coefficiente moltiplicativo del PLL interno al DSP e quindi la frequenza del clock che arriva alla CPU è la stessa di quella generata dall’oscillatore esterno, ovvero 10 MHz. Si evince dalla temporizzazione per gli accessi alla memoria esterna che tra una accesso e l’altro devono passare almeno due cicli di clock perché l’handshake sia completato correttamente, per la precisione passa un tempo pari ad almeno:

ns ns ns ns t t

TCLKOUT d CLKH ISTRBL d CLKH ISTRBH 200 4 5 191

2⋅ + ( )( ) = − − =

Questo tempo è il tempo che passa da quando /IOSTRB sale per la fine dell’accesso allo spazio esterno, a quando ridiscende per scandire l’accesso successivo. Entro questo tempo quindi i dati devono essere già andati stabili alle uscite del registro. Dal data-sheet del registro di otto Flip Flop D-Edge Triggered 74LV574, si ricava il tempo che intercorre tra la salita del clock e la presentazione delle uscite, ovvero tPLH/tPHL.

(10)

Figura 5: Tempi di ritardo tra la variazione del clock e l’uscita del 74LV574

Nel caso peggiore, quindi, il risultato è pronto alle uscite per essere memorizzato già 13 ns dopo la risalita dell’/IOSTRB e quindi viene rispettata la specifica poc’anzi esposta. Si vede dunque che la temporizzazione rispetta quanto da noi richiesto e di conseguenza la variabile Status_574 risulta correttamente memorizzata e mantenuta verso l’esterno. Questa operazione viene ripetuta per cento volte e quindi, essendo globalmente l’accesso allo spazio di memoria esterno della durata di tre cicli di clock, si ha che la configurazione di segnali che viene mandata al codec è mantenuta per:

s ns 30µ 100

3

100× × =

Vedremo, parlando della programmazione del codec, che questo tempo, in cui il dato viene mantenuto, rispetta pienamente le sue tempistiche di programmazione.

(11)

Trascorso tale tempo viene ripristinato MP3 a 1 logico, sempre con la tecnica dell’OR e secondo le modalità precedentemente descritte. Anche questo livello viene mantenuto al codec per 30 µs. La temporizzazione dei segnali che sono mandati al codec durante l’esecuzione di tale funzione risulta quindi essere come mostrato nella figura 6 (sopra). Si vedrà più avanti, quando si parlerà della programmazione del codec, che il segnale MP3 corrisponde all’L3CLOCK e il segnale MP4 corrisponde all’L3DATA; vale così quando si lavora nella modalità L3 microcontroller, come nel nostro caso.

L’indirizzo dello spazio esterno nel quale viene scritta la variabile di memoria non ha importanza perché soltanto il registro 574 è comandabile nello spazio di I/O in scrittura, e quindi non serve alcun indirizzamento; si mette qui l’indirizzo 0000h, ma potrebbe avere un valore qualsiasi.

Per quanto riguarda le altre funzioni che vengono usate nella programmazione dell’UDA, ovvero: SendZeroToUDA(void), SetAddrMode(void), SetDataMode(void), ResetAddrMode(void) e ResetDataMode(void), queste agiscono sui segnali interessati con lo stesso principio, e quindi non verranno trattate. Si darà, nel paragrafo dedicato, completa descrizione del processo di programmazione del codec.

4.1.2 Dichiarazioni

Tale sezione dedicata alle dichiarazioni comprende le seguenti parti, come si vede nel diagramma di flusso presentato all’inizio:

Ata Drive Register adresses

In questa sezione vengono assegnati dei nomi simbolici agli indirizzi dei registri interni del controllore della flash card, o meglio solo a quei registri coinvolti nel trasferimento dati, presentati nel capitolo dedicato alle Compact Flash Card. Così facendo si migliora decisamente la leggibilità del programma perché nel comando di scrittura o lettura si può usare il nome del registro stesso senza dover usare l’indirizzo del registro.

(12)

Constant declaration

Questa sezione è molto importante perché dai valori delle costanti qui inserite possono dipendere le prestazioni di tutto il sistema. Infatti possiamo impostare in questa sezione, tra gli altri parametri, anche la lunghezza del buffer della SRAM esterna al DSP, il cui dimensionamento (decisamente critico) è stato discusso nel capitolo dedicato all’hardware.

Per come sono state impostate le variabili a questo punto si hanno, tra le assegnazioni più importanti:

asm("Dim_Buffer_Sram .set 04000h");

asm("Dim_Buffer_Flash .set 00100h");

asm("Num_Sectors_of_Flash_Hw .set 00001h");

asm("Num_Sectors_of_Flash_Lw .set 03abch");

asm("Num_Sectors_to_Access .set 00020h");

1. Dim_Buffer_Sram

Come già discusso nel capitolo del debug della SRAM, si impostano qui le dimensioni del Buffer per la SRAM a 4000h, che corrispondono a 32 kB (perché le locazioni sono da 16 b). Ridurre questo parametro mantenendo le dimensioni di potenza di due (cioè portare le dimensioni di questo a 16 kB) significa, come già spiegato, compromettere il funzionamento del sistema.

2. Dim_Buffer_Flash

Questo parametro viene settato in questo modo perché si considera di default il buffer interno della flash card delle dimensioni di 100h, ovvero di 256 word (ricordiamo che il trasferimento dati della flash card viene gestito a 16 b), 512 B che poi sono la dimensione di un settore. È stato scelto questo valore perché è il minimo consentito in quanto non esistono buffer più corti di un settore. Qualora il buffer fosse di due o più settori questi verranno riempiti con più cicli trasferendo comunque un settore

(13)

buffer interno della flash (come visto nel capitolo dedicato alle flash card) e quindi con tale informazione si potrebbe aggiornare tale costante, che a questo punto sarebbe una variabile. Ad ogni modo, se tale parametro viene lasciato delle dimensioni di un settore, il sistema funziona lo stesso e per tutti i tipi di flash card.

3. Num_Sectors_of_Flash_Hw & Num_Sectors_of_Flash_Lw

Queste due parole sono una la parte alta e l’altra la parte bassa del numero che indica di quanti settori è composta la flash card. Nel nostro caso abbiamo impostato, per default:

Num_Sectors_of_Flash_Hw = 0001h Num_Sectors_of_Flash_Lw = 3abch

Che corrispondono ad un numero di settori di:

80572 15036 65536 15036 2 1 ) 3 ( 2 ) 0001 ( h × 16 + abch = × 16 + = + =

Che di per sé, essendo la dimensione di un settore di 512 B,

indicherebbero una capacità della flash card di:

MB kB kB B B 40286 39,34 1024 41252864 41252864 512 80572× = ⇒ = ≅

Ovviamente non tutte le flash card hanno queste dimensioni, questa è solo una assegnazione di default. Inoltre nel codice più avanti è presente un comando in grado di leggere le caratteristiche della card e con tali dati si modificano i parametri nel codice in modo da adattarlo alla flash card in uso.

(14)

4. Num_Sectors_to_Access

Questa costante serve per essere successivamente copiata nella variabile Num_Sec_Count_Reg che è la variabile che si usa per inizializzare il registro Sector Count Register (si veda in proposito il capitolo 1 dedicato alle flash card). In tale registro viene scritto il numero di settori che si vuole trasferire ogni volta che viene inoltrato il comando di lettura o scrittura. Nel nostro progetto si è preferito inizializzare tale variabile a 20h (32) perché, dalle prove effettuate, si è visto che non tutte le flash card supportano il trasferimento di un maggior numero di settori con un unico comando. Questo del resto non è un problema perché il codice è ben strutturato e quindi è in grado di inoltrare ogni volta il comando per il trasferimento di trentadue settori, finché non sono stati trasferiti tutti i settori voluti.

Vi sono anche, sempre in questa sezione, alcune costanti nella sottosezione Flash Identify Buffer, che sono gli indirizzi relativi usati all’interno del buffer della flash per riscrivere alcune caratteristiche della flash con il comando apposta. Quest’operazione non sembra aver apportato miglioramenti all’andamento del sistema e quindi non è stata inserita nella release finale del codice.

Variables declaration

In questa sezione vengono dichiarate le variabili che verranno poi usate nel codice: gli ingressi e le uscite per il codec, gli Status dei registri 574 e 244, i campioni che sono mandati e ricevuti dalla flash, le variabili che vengono scritte nei registri della flash (sia per l’accesso a 8 b che per l’accesso a 16 b), vari flag per la gestione del codice durante l’esecuzione, variabili che vengono aggiornate dalla lettura delle caratteristiche della flash tramite il comando Identify Drive e variabili che vengono usate per la gestione dei comandi di ingresso.

(15)

Functions declaration

Qui vengono dichiarate le funzioni viste prima per il codec e altre variabili e puntatori di supporto.

4.1.3 Internal clock initialization (A)

In tale sezione vengono inizializzati i registri di stato del DSP: ST1 e PMST, in modo tale che il programma funzioni correttamente. Tali registri contengono dei flag che riguardano la precisione, gli overflow, i carry, il data page pointer ecc. Il settaggio è stato fatto per operare con interi; del resto in tale codice non vengono fatte moltiplicazioni e non vengono usati float (numeri a doppia precisione) che avrebbero richiesto un diverso settaggio di tali flag.

Dopo questa parte si trova la seguente sequenza di comandi usati per il settaggio della frequenza di clock:

asm(" stm #0b, CLKMD"); // clock

asm("PLL_ON_INIT:");

asm(" ldm CLKMD, a");

asm(" and #01h, a");

asm(" bc PLL_ON_INIT, ANEQ");

asm(" stm #00007h, CLKMD"); //Dsp Int Clk=Mck

asm(" stm #00007h, CLKMD"); //Se si usa TMS320VC5401-50

Con questi comandi si setta il registro CLKMD caricandovi un valore pari a 00000111 b per quanto riguarda il byte meno significativo. Si vede dalle specifiche che riguardano l’inizializzazione del registro CLKMD, che viene riportato nella seguente figura:

(16)

che il bit PLLON/OFF viene messo a uno, come PLLNDIV e PLLSTATUS, mentre il resto è a zero. Nella seguente figura viene riportata la parte del data-sheet che spiega i significati di questi campi:

(17)

Da come è impostato tale registro si ha che è attiva e funzionante la modalità PLL. Volendo poi impostare il clock della CPU alla stessa frequenza del clock del quarzo, almeno in questo tratto di codice dove vanno inizializzate le componenti hardware e quindi ci si trova “offline” rispetto alle procedure, permettendosi quindi di andare più lenti per non fornire all’hardware circostante dei frame di dati troppo veloci per la programmazione, si è deciso di impostare gli altri campi perché il fattore di moltiplicazione del PLL fosse pari a uno. Si pone quindi: PLLNDIV pari a uno, PLLDIV pari a zero, e PLLMUL pari a zero. Questo corrisponde, secondo la tabella seguente:

Figura 9: Fattore di moltiplicazione del PLL

ad impostare ad uno il fattore di moltiplicazione del PLL. Nella sezione successiva relativa alla modifica del fattore di moltiplicazione del PLL, questo verrà portato a cinque e quindi il clock della CPU verrà portato a 50 MHz per rendere più veloce l’esecuzione delle procedure.

(18)

4.1.4 CPU registers initialization

In questa sezione abbiamo una serie di inizializzazioni dei registri interni della CPU necessarie per farla operare nella modalità richiesta, ovvero per gestire correttamente gli interi con segno.

Dopo questa parte si ha l’inizializzazione del generatore di stati di attesa programmabile SWWSR che, come già detto nel capitolo dedicato all’hardware, serve per allungare eventualmente i cicli esterni del bus fino a quattordici cicli macchina. Con l’istruzione:

asm(" STM #0000dh, SWWSR"); // 5 cicli di wait (con IntClk=10xMClk)

asm(" ld #00h, dp");

asm(" STM #01h, 2bh"); // 14 cicli di wait (con IntClk=10xMClk)

si carica 000dh, ovvero 1101 b per quello che riguarda il nibble meno significativo, nel registro SWWSR. Inoltre viene caricato un uno nel campo SWWSM del registro SWCR (mappato all’indirizzo 2bh) che significa che il numero di stati di attesa impostati corrisponde a quelli scritti nel registro SWWSR ma moltiplicati per due.

Figura 10: Il Registro SWWSR

(19)

Quindi, come si vede dalle specifiche sul data-sheet, i cicli di attesa agli spazi di memoria esterna sono impostati nel modo seguente:

 Due soli cicli di attesa per gli accessi allo spazio di memoria programma che ha gli indirizzi che vanno da 8000h a FFFFh, in quanto ne è stato impostato uno solo nel registro SWSSR ma essendo SWSM pari a uno allora vengono moltiplicati per due. In questo spazio di memoria, come si vede nella seguente figura, viene mappata parte della SRAM esterna, infatti si ha:

(20)

Quindi si ha che la memoria esterna, che è quella alla quale accediamo noi, sarebbe effettivamente mappata nello spazio di memoria che va da 4000h a EFFFh. Tale spazio è compreso in parte nello spazio di memoria programma che va da 8000h a FFFFh e quindi sembrerebbe che l’accesso a questo spazio, e quindi alla nostra SRAM, sia fatto interponendo due stati di attesa per ogni accesso. In realtà, come già detto nel capitolo dedicato alla descrizione dell’hardware, della SRAM se ne utilizza solo una piccola parte, un buffer da 32 kB. Tale dimensione in esadecimale corrisponde a 4000h. Si vede quindi che, essendo l’indirizzo di base della SRAM mappato all’indirizzo iniziale nel quale comincia la memoria esterna secondo la mappatura di memoria, l’indirizzo finale del buffer di SRAM corrisponderà all’indirizzo base sommato alla dimensione del buffer, ovvero 8000h (4000h + 4000h) escluso. Quindi di questa sezione di memoria (che va da 8000h a FFFFh) nella quale abbiamo due cicli di attesa per ogni accesso, non fa parte il buffer di SRAM da noi considerato perché questo finisce all’indirizzo 7FFFh

 Dieci cicli di attesa per gli accessi allo spazio di memoria programma che ha gli indirizzi che vanno da 0000h a 7FFFh, in quanto ne sono stati impostati cinque nel registro SWSSR ma essendo SWSM pari a uno allora vengono moltiplicati per due. Questo spazio di memoria programma comprende effettivamente parte della memoria esterna ma soprattutto comprende completamente il buffer di SRAM utilizzato nel progetto, che avendo indirizzo base in 4000h ed avendo dimensione pari a 4000h va a coprire un range di indirizzi dello spazio di memoria che va da 4000h a 7FFFh, compreso all’interno del range considerato.

Vengono quindi impostati dei cicli di attesa, in modo che il DSP possa interfacciarsi con la SRAM. Se non si fosse operato in questo modo, avendo la SRAM dei tempi di risposta troppo lenti per il DSP, la stesura del codice sarebbe stata più complicata perché si sarebbero dovuti inserire (manualmente via codice) dei tempi di attesa per il

(21)

4.1.5 McBSP 2xSerial Ports Initialization

Come detto precedentemente questa sezione è importantissima perché tramite l’impostazione delle porte seriali si riesce a dare la corretta temporizzazione al codec in modo che quest’ultimo funzioni correttamente. Le assegnazioni ai registri che si fanno servono per dire al DSP come configurare il circuito interno del Sample rate generator, in modo da impostarlo come già visto, ovvero:

(22)

oltre che per impostare i flag e le modalità di funzionamento di tale circuiteria. La parte più interessante di tale inizializzazione risiede nella scelta del valore da impostare nei campi CLKGDV (che si trovano nei registri SRGR1 di entrambe le porte seriali), che poi non indicano altro che il fattore di divisione per quello stadio divisore sia per la porta seriale 0 che per la porta seriale 1, nonché nella scelta del valore da impostare nei campi FPER (che si trova nel registro SRGR2) e FWID (che si trova nel registro SRGR1) per la porta seriale 0. Infatti è tramite questi campi che si imposta l’andamento della forma d’onda di uscita dai pin BCLKX0 e BCLKX1 che sono le uscite CLKG per le porte 0 e 1, e dai pin BFSX0 e BFSX1 che sono le uscite FSG per le porte 0 e 1 (si veda la figura 13 nel capitolo dedicato all’hardware). Nella seguente figura 14 si vede la relazione (in generale) che intercorre tra il segnale che esce dal pin BCLKX0 con il segnale che esce dall’uscita BFSX0 (chiamato FSG nella figura) che nel nostro circuito va a pilotare il segnale WS:

Figura 14: Programmazione del periodo e dell’ampiezza del frame

Si vede che tale relazione dipende da come sono programmati i registri SRGR1 e SRGR2 per entrambe le porte seriali. Bisogna innanzitutto chiarire in che relazione temporale devono essere questi segnali perché il codec sia interfacciato correttamente. Considerando la figura 13 di pagina precedente si vede che il CPU clock (che si è visto essere pari a 50 MHz) entra nel sample rate generator di entrambe le porte seriali, per come è stato impostato il sistema. Volendo ottenere una frequenza di campionamento di circa 40 KHz si ha quindi un vincolo sulla frequenza da mandare al codec come sysclk (il segnale MCK1 nella figura 13). Infatti, dalle specifiche del codec, sappiamo che la frequenza con la quale si campionano i segnali (stereo) è una frazione della frequenza

(23)

Figura 15: Rapporto tra frequenza che entra nel sysclk e frequenza di campionamento

Poiché (si veda nella successiva sezione dedicata alla programmazione del codec) si è scelto che valga SC1 = 1 e SC0 = 0, la frequenza di campionamento risulta essere:

256

sysclk fS =

Come detto più volte la frequenza di campionamento che si ha in questo sistema è pari a 39062.5 Hz. Si ha quindi che la frequenza del segnale sysclk (che poi corrisponde all’uscita MCK1 del sample rate generator della Porta seriale 1) deve essere pari a:

MHz Hz

Hz f

sysclk = S ×256=39062.5×256 =10000000 =10

Dal momento che la frequenza che entra nei sample rate generator è di 50 MHz è sufficiente far dividere per cinque il primo stadio di divisione del sample rate generator della porta seriale 1. Questa operazione viene attuata caricando un valore pari a quattro nel campo CLKGDV (come si legge poco più avanti nel codice) nel registro SRGR11, in quanto sussiste la seguente relazione tra le frequenze, secondo le specifiche del data-sheet: 1 + = CLKGDV clock CPU CLKG

(24)

dove CLKG è il segnale che si ha all’uscita del primo stadio divisore, ovvero l’uscita hardware BCLKX per le porte seriali 0 e 1.

Nella seguente figura si vede cosa comprende il registro SRGR1:

Figura 16: SRGR1

Andando a vedere il codice si trova che nella parte bassa del registro SRGR11 (Porta seriale 1) viene caricato proprio un valore pari a quattro. Cosa si scrive nella parte alta, ovvero nel campo FWID, non ha importanza, almeno per quanto riguarda la Porta seriale 1.

// Serial Port 0 Configuration … asm(" rpt #6"); asm(" nop"); asm(" stm #06h, 038h"); // SPSA <-- SRGR10 asm(" stm #0f27h, 039h"); // Se Dsp Int Clk=5xMck, Fs=39kHz asm(" rpt #6"); asm(" nop"); asm(" stm #07h, 038h"); // SPSA <-- SRGR20 asm(" stm #0301fh, 039h"); // SPSD <-- 301fh asm(" rpt #6"); asm(" nop"); …

// Serial Port 1 Configuration … asm(" rpt #6"); asm(" nop"); asm(" stm #06h, 048h"); // SPSA <-- SRGR11 asm(" stm #0f04h, 049h"); // Se Dsp Int Clk=5xMck, Fs=39kHz asm(" rpt #6"); asm(" nop"); asm(" nop"); asm(" stm #07h, 048h"); // SPSA <-- SRGR21 asm(" stm #0301fh, 049h"); // SPSD <-- 301fh asm(" rpt #6");

(25)

La scelta di come impostare i registri SRGR10 e SRGR20 è diretta conseguenza di quanto fatto per la Porta seriale 1.

Facendo sempre riferimento alla figura 13 di questo capitolo e a quanto detto nel capitolo dedicato all’hardware si vede che il segnale BCLKX0, che è collegato all’ingresso BCK del codec, fornisce il sincronismo di bit per la trasmissione seriale dei dati dal codec al DSP. Quindi, poiché ad ogni istante di campionamento si devono trasmettere 32 b trattando segnali stereo con conversione a 16 b, questo segnale si può settare in modo che abbia una frequenza pari a:

MHz Hz

Hz f

BCLKX0= S ×32=39062.5×32 =1250000 =1.25

Questa frequenza si ottiene dividendo il CPU clock per quaranta, infatti:

clock CPU MHz

MHz

BCLKX0×40=1.25×40 =50 =

Per far ciò si carica, equivalentemente a quanto fatto prima, il valore di 39 decimale nel campo CLKGDV del registro SRGR10, come si vede anche nel comando dedicato (27h corrisponde a 39 in decimale).

Inoltre c’è anche da settare il valore dei campi FPER e FWID (per la Porta seriale 0) perché anche da questi dipende la temporizzazione del WS. Il WS è il segnale che dà il sincronismo di parola; si usa per distinguere le parole che provengono dal canale destro da quelle che provengono dal canale sinistro (come già visto quest’operazione si fa testando l’ingresso BIO al quale via hardware è collegato il WS). La distinzione si fa testando il livello logico del WS; è quindi necessario che tale segnale sia alto per i sedici campioni provenienti da un canale e basso per i successivi sedici campioni provenienti dall’altro canale. Quindi il periodo di tale segnale deve essere trentadue volte il periodo del segnale BCLKX0 (ovvero il BCK del codec) e ciò si ottiene impostando a trentuno il campo FPER del registro SRGR20, come si vede dalla figura 14 di questo capitolo. Il registro in questione ha i campi definiti come si vede nella figura seguente:

(26)

Figura 17: SRGR2

Nel codice si ritrova quanto detto, infatti si carica nel campo FPER del registro SRGR20 il valore di 01fh che corrisponde appunto a 31 decimale.

Quanto fatto non basta perché è anche necessario che il segnale WS abbia un duty cycle del 50% perché entrambi i campioni provenienti dal canale destro e sinistro hanno un formato a 16 b. Quindi se il segnale WS ha un periodo che dura trentadue periodi del segnale CLKG è necessario che il tempo in cui questo segnale resta alto duri per sedici periodi del segnale CLKG. Ciò, come già fatto e come si vede dalla figura 14, si ottiene caricando quindici nel campo FWID del registro SRGR10, ovvero la sua parte alta. Ancora una volta questo trova rispondenza nel codice perché si vede dal comando relativo che si carica 0fh, ovvero 15 decimale, in tale campo.

In definitiva la forma d’onda che si ottiene in uscita, per quanto riguarda il BCK e il WS, ha il seguente andamento:

Figura 18: Relazione temporale tra il BCK e il WS

Tali forme d’onda per questi segnali pilotano correttamente il codec.

Il ragionamento seguito per impostare la frequenza di campionamento a 39062.5 Hz è identico al ragionamento necessario per impostare i campi dei registri in modo da

(27)

flash card. Chiaramente i valori scritti in questi campi saranno diversi ma la proporzione tra loro sarà la stessa di quella vista per la frequenza di 39062.5 Hz, in quanto tra i segnali MCK1, BCLKX0 e WS sussiste sempre la stessa relazione.

4.1.6 UDA1345TS Codec Configuration

Come già detto precedentemente nella fase di definizione delle funzioni che si usano per la programmazione del codec si utilizza il registro 74LV574 per mandare i segnali necessari. Per questo per prima cosa si inizializza la variabile Status_574 mandando a 0 MP2, MP3 e MP4. Considerando che si lavora in modalità L3 microcontroller (modalità di funzionamento dovuta al fatto che MC1 e MC2 sono collegati a GND) si hanno le seguenti corrispondenze tra i pin esterni e i segnali di input:

Figura 19: L3 microcontroller mode

Così facendo si riescono a pilotare i segnali che servono alla programmazione, ovvero L3MODE, L3CLOCK e L3DATA. All’interno della modalità L3 si possono distinguere due ulteriori modalità di funzionamento: l’Address mode e il Data transfer mode.

(28)

Address mode

L’Address mode è richiesto per selezionare il dispositivo che comunica tramite l’L3-bus e per definire il registro di destinazione settato (Data o Status) per il Data transfer mode immediatamente seguente. Di seguito la temporizzazione tipica di tale Address mode:

Figura 20: Timing Address mode

Per l’UDA1345TS l’indirizzo è 000101 b (sono i sei bit più significativi del dato che viene trasmesso) e con gli ultimi due bit si decide se il Data transfer mode immediatamente successivo trasmette un Data o uno Status, secondo la seguente tabella:

(29)

Nel nostro caso, come si vede dal codice relativo (riportato più avanti) si vede che il primo byte che viene mandato al codec in modalità Address mode vale 0x16, ovvero 00010110 b, e quindi il byte che è mandato nel successivo Data transfer mode è un byte di Status.

Data transfer mode

Il Data transfer mode, per l’UDA1345TS, può essere solo in una direzione: i dati possono essere solo scritti nel dispositivo. La selezione del tipo di byte di dato fatta nel precedente Address mode rimane attiva durante tutti i seguenti trasferimenti di dati nel Data mode, questo fino ad un nuovo trasferimento in modalità Address mode. La temporizzazione di tale modalità è praticamente uguale quella dell’Address mode e viene riportata nella figura seguente:

Figura 22: Timing for Data transfer mode

I dati che si trasmettono in tale modalità vengono salvati in registri diversi a seconda del tipo di dato, Data o Status, che si sceglie nel precedente Data Address mode come visto. Quando si trasmette il byte di Status si trasmettono le seguenti informazioni:

(30)

Figura 23: Data transfer of type Status

Quando invece si trasmettono i byte di dato, si trasmettono le seguenti informazioni (vanno trasmessi in sequenza):

Figura 24: Data transfer of type Data

Come si vede dalle temporizzazioni, perché i dati vengano passati correttamente, bisogna che i bit che vengono trasmessi sul segnale L3DATA siano clockati dal segnale L3CLOCK. Quest’operazione viene ingegnosamente compiuta dalle funzioni per la programmazione del DSP in quanto ognuna di queste funzioni agisce su L3MODE e su L3DATA e contemporaneamente dà la temporizzazione al segnale L3CLOCK, mandandolo alto per 30 µs e poi di nuovo basso per altri 30 µs. Questi tempi sono più che sufficienti per il codec, che da specifiche richiede dei tempi sull’L3CLOCK come si vede nella seguente figura:

(31)

Viene riportato qui di seguito il codice per intero per questa sezione di programmazione del codec:

// Prepara la variabile Status_574 per la programmazione del Codec

asm(" ld #Status_574, dp");

asm(" ld Status_574,a");

asm(" and #0f8h, a"); // Mode, Clock, Dato = 0

asm(" stl a, Status_574");

ToUDA = 0x16; // Seleziono l'invio dello status

SetAddrMode(); for (i=0; i<8; i++) { if (ToUDA & 1) SendOneToUDA(); else SendZeroToUDA(); ToUDA >>= 1; } ResetAddrMode();

ToUDA = 0x29; //0x28=PA Off, 0x290PA On

SetDataMode(); for (i=0; i<8; i++) { if (ToUDA & 1) SendOneToUDA(); else SendZeroToUDA(); ToUDA >>= 1; }

ToUDA = 0x14; // Seleziono l'invio dei data

SetAddrMode(); for (i=0; i<8; i++) { if (ToUDA & 1) SendOneToUDA(); else SendZeroToUDA(); ToUDA >>= 1; } ResetAddrMode(); ToUDA = 0x00; SetDataMode(); for (i=0; i<8; i++) { if (ToUDA & 1) SendOneToUDA(); else SendZeroToUDA(); ToUDA >>= 1;

(32)

}

ToUDA = 0x40; SetDataMode(); for (i=0; i<8; i++) { if (ToUDA & 1) SendOneToUDA(); else SendZeroToUDA(); ToUDA >>= 1; } ToUDA = 0x80; SetDataMode(); for (i=0; i<8; i++) { if (ToUDA & 1) SendOneToUDA(); else SendZeroToUDA(); ToUDA >>= 1; } ToUDA = 0xC3; SetDataMode(); for (i=0; i<8; i++) { if (ToUDA & 1) SendOneToUDA(); else SendZeroToUDA(); ToUDA >>= 1; }

Si vede dal codice che noi mandiamo in sequenza al codec (dall’inizio della procedura fino alla fine) in ordine cronologico:

1) Address mode ⇒ 0x16 ⇒ 00010110 b ⇒ invio Status

2) Data mode ⇒ 0x29 ⇒ 00101001 b

3) Address mode ⇒ 0x14 ⇒ 00010100 b ⇒ invio Data

4) Data mode ⇒ 0x00 ⇒ 00000000 b

5) Data mode ⇒ 0x40 ⇒ 01000000 b

(33)

Tenendo sempre presente quindi le figure precedenti 23 e 24 e andando a verificare cosa vogliono dire le assegnazioni (dalle pagine del data-sheet) si ha che nei precedenti punti si programmano i seguenti parametri:

1) Address mode ⇒ 0x16 ⇒ 00010110 b ⇒ invio Status

2) Data mode ⇒ 0x29 ⇒ 00101001 b ⇒ sysclk =256fS, Data input format è MSB-justified, il DC filtering è attivo.

3) Address mode ⇒ 0x14 ⇒ 00010100 b ⇒ invio Data

4) Data mode ⇒ 0x00 ⇒ 00000000 b ⇒ Volume settings è 0dB (nessuna

attenuazione)

5) Data mode ⇒ 0x40 ⇒ 01000000 b ⇒ Non usato

6) Data mode ⇒ 0x80 ⇒ 10000000 b ⇒ Non si ha né de-emphasis né muting

7) Data mode ⇒ 0xC3 ⇒ 11000011 b ⇒ Sono sempre attivi sia l’ADC che il

DAC

4.1.7 Flash Card Init

In questa sezione si provvede a inoltrare un comando di reset hardware tramite il pin card_rst direttamente collegato all’uscita del registro 74LV574. Quando si è incontrato il problema della flash card che non rilasciava il ready si è pensato di mandare il reset (tra le varie prove fatte) anche via software tramite il settaggio del Configuration Option Register. Questa prova non diede esito positivo. Il problema si risolve sequenzializzando opportunamente l’attivazione delle alimentazioni ai vari dispositivi, lasciando per ultima la compact flash card (come già detto in altri punti in questo lavoro di tesi).

(34)

4.1.8 Inizializzazione SRAM

Tramite i comandi presenti in questa sezione si “ripulisce” tutta la zona della SRAM esterna che si è deciso di usare come buffer per la flash card, copiando il valore contenuto nella variabile Smpl_FromFlash1 (che a questo livello è ancora pari a zero per l’inizializzazione delle variabili fatta inizialmente) in tutto il buffer. Tale operazione è necessaria per evitare, al primo trasferimento (il cui meccanismo verrà illustrato in seguito) in fase di riproduzione, l’invio di dati casuali al codec, che darebbero inevitabilmente del rumore fastidioso.

4.1.9 Others Init

In questa sezione si inizializzano: le variabili per l’accesso alla flash card a 8 e a 16 b (noi useremo in questa release del software sempre l’accesso a 16 b), le variabili che indicano la capacità della flash card, ovvero le variabili che si useranno sia nei processi di lettura che di scrittura per vedere quando si è arrivati in fondo alla flash card, e dei flag relativi alla sezione comandi, che vengono inizializzati in questa sezione in modo da rendere conto del fatto che si è all’avvio, e quindi l’eventuale scrittura o lettura è la prima. I flag in questione vengono settati nel seguente modo:

Flag_first_wr_rd = 0 ⇒ È la prima lettura/scrittura

Flag_start_stop = 0 ⇒ All’avvio si è nella condizione di Stop

(35)

4.1.10 Internal clock initialization (B)

In questa sezione, simile in tutto alla precedente Internal clock initialization (A), viene settato il moltiplicatore interno a PLL per moltiplicare la frequenza che viene generata tramite il circuito oscillatore interno (con il quarzo che viene collegato tra i piedini X1 e X2/CLKIN) per cinque. Quindi, poiché il quarzo è da 10 MHz si ha che il clock con il quale lavora la CPU diventa 50 MHz da questa sezione di codice in poi. Il codice relativo è il seguente: asm(" ld #0h, dp"); asm(" stm #0b, CLKMD"); // clock asm(" stm #0b, CLKMD"); asm("PLL_ON:"); asm(" ldm CLKMD, a");

asm(" and #01h, a");

asm(" bc PLL_ON, ANEQ");

asm(" stm #04007h, CLKMD"); //Dsp Int Clk=5xMck

asm(" stm #04007h, CLKMD"); //Se si usa TMS320VC5401-50

Si nota subito che l’unica differenza rispetto alla precedente inizializzazione è l’aver impostato a quattro il campo PLLMUL del registro CLKMD. Ciò, come si vede dalla figura seguente, implica un coefficiente moltiplicativo del PLL pari a cinque.

(36)

Si è attuata questa duplice inizializzazione del clock interno al DSP perché in fase iniziale non importa avere un clock veloce, in fondo per l’utente non fa alcuna differenza se all’avvio deve aspettare 100 ms o 500 ms per poter utilizzare il CF-P/R. Tale opzione ha però permesso di poter programmare il codec e i registri interni del DSP con più ampio margine sui tempi da rispettare, riducendo criticità di programmazione dello codec stesso.

4.1.11 Identificazione delle caratteristiche della flash

In questa sezione si inoltra alla Compact Flash Card il comando “Identify drive”, la cui descrizione dettagliata è stata fatta nel capitolo dedicato alle flash card, e tramite questo si acquisiscono informazioni riguardo la flash card utilizzata in quel momento nel sistema. Con questi parametri si modificano alcune variabili che vengono usate per gestire il codice cercando di ottimizzarne le prestazioni rispetto alla Compact Flash Card in esame. Nella figura seguente si vede il diagramma di flusso per questa sezione:

“Flash_card_init”

NO

SI

Continua da sezione precedente

La flash card è presente?

(37)

“Wait_ready_Id_Rd” “Flash_card_init” SI NO NO SI “Repeat_Command_Id_Rd” “Wait_DRQ_Id_Rd” SI NO NO SI È avvenuto un errore (generico) nella flash

card?

La flash card è pronta per accettare

comandi?

Viene inoltrato il comando “Identify Drive” alla flash card

È avvenuto un errore (generico) nella flash

card?

La flash è pronta e ha attivato il DRQ per fare il

trasferimento dei dati richiesti?

(38)

“Continue_read_Sector_Id” SI NO “Not_Restart_ID_To_Sram” NO SI “Init”

Le procedure di testing dei registri interni della flash card e dello stato dell’octal buffer 74LV244 usate in questa sezione di codice nella quale si è inoltrato il comando “Identify Drive” sono le stesse che poi sono usate in tutto il resto del codice per inoltrare gli altri comandi. Infatti si opera sempre inizialmente un test per verificare la presenza della Compact Flash Card, tramite la seguente sequenza dei comandi:

// Verifica presenza Flash

asm(" ld #Status_244, dp");

È stato riempito tutto il BufferID di SRAM?

Si preleva dato dal buffer interno della flash e lo si mette nel buffer di SRAM nel BufferID

Si azzera il puntatore Buffer_ID (SRAM)

Il puntatore che scorre nel settore della flash ha scorso

tanti dati quanti sono permessi dalla dimensione

del Buffer_ID?

Inizializzazione delle variabili relative ai parametri della flash card

(39)

cioè andando a vedere il livello logico del piedino D3 del bus dati sul quale va a finire un livello logico basso solo se la flash card è correttamente inserita, come già spiegato nel capitolo dedicato alla descrizione dell’hardware.

Figura 27: L’octal buffer 74LV244

Dopodiché, per verificare se è avvenuto un qualsiasi tipo di errore nella flash si va a leggere il contenuto del registro Status Register (descritto ampiamente nel capitolo dedicato alle flash card) e tramite un test sull’ultimo bit, il bit ERR, si vede se c’è stato un errore e in caso affermativo si torna alla sezione di inizializzazione della flash card, nella speranza che ripercorrendo tutta la procedura di inizializzazione non si incorra in un altro errore. La sezione comandi che gestisce questa parte è la seguente:

//Wait Ready

asm(" ssbx 1, xf");

asm("Wait_Ready_Id_rd");

asm(" ld #Fc_XPC_Setting, 16, a");// carico estens addr in acc

asm(" or #Fc_AddOffset_5401, a"); // definisco offset address

asm(" or #Drv_HdStatusCommand_Reg_Address, a");// def indirizzo

asm(" portr #0000h, IO_Idle_Data"); asm(" reada Smpl_FromFlash");

asm(" portr #0000h, IO_Idle_Data");

asm(" bitf Smpl_FromFlash, #0100h");

(40)

Il test successivo viene fatto sul bit READY e sul bit DSC, dove il primo dice se la flash è in grado in quel momento di effettuare operazioni e il secondo dice se la flash card è pronta. Questi bit si possono sempre verificare dallo Status Register, con la seguente sequenza di comandi:

asm(" reada Smpl_FromFlash");

asm(" portr #0000h, IO_Idle_Data");

asm(" ld Smpl_FromFlash, a");

asm(" and #0f000h, a");

asm(" sub #05000h, a");

asm(" bc Wait_Ready_Id_rd, aneq");

Dopodiché si può inoltrare il comando “Identify Drive” e per far questo è necessario scrivere il comando nel Command Register della flash card. Nel nostro codice si opera inserendo il codice macchina del comando che si vuole inoltrare in una apposita variabile per poi copiare tale variabile nel Command Register.

La parte di codice relativa è la seguente:

asm("Repeat_Command_Id_rd");

asm(" ld #0ec00h, a");//identify drive

asm(" add Drv_Hd_Reg, a");

asm(" stl a, Drv_Hd_RegCommand_Reg");

// Registers Init Asm Code Section

asm(" ld Drv_Hd_RegCommand_Reg, a");

asm(" stl a, Smpl_ToFlash");

asm(" ld #Fc_XPC_Setting, 16, a");

asm(" or #Fc_AddOffset_5401, a");

asm(" or #Drv_HdStatusCommand_Reg_Address, a");

asm(" writa Smpl_ToFlash"); //Drv_Hd_Reg=e0h

asm(" portr #0000h, IO_Idle_Data");

Dopo aver mandato il comando bisogna assicurarsi che non avvengano errori nella flash card perché se così fosse sarebbe necessario inoltrare nuovamente il comando perché l’errore incorso ne potrebbe falsare la corretta ricezione. Quindi si fa un ulteriore test sul bit ERR dello Status Register (identico al precedente) e in caso di errore si ritorna alla sezione nella quale si inoltra il comando “Identify Drive”.

(41)

aspettando che i dati vengano trasferiti. Il bit DRQ si trova sempre nello Status Register, e la sezione di comandi utilizzata per il test (molto simile alle precedenti) è:

asm(" reada Smpl_FromFlash");

asm(" portr #0000h, IO_Idle_Data");

asm(" ld Smpl_FromFlash, a");

asm(" and #0f800h, a");

asm(" sub #05800h, a");

asm(" bc Wait_Drq_Id_rd, aneq");

Alla fine di tale test, se il DRQ non è ancora stato attivato dalla flash card, si ritorna a verificare se è avvenuto un errore, come si vede dal diagramma di flusso.

Dopo questa parte inizia la procedura vera e propria di lettura del buffer interno della flash contente i dati dell’“Identify Drive” e relativa scrittura di questi in una porzione di SRAM, dalla quale poi verranno prelevati per inizializzare le variabili relative alle caratteristiche della flash card.

Le modalità di caricamento dei registri necessari all’inoltro dei comandi di scrittura e lettura, in termini di comandi assembler, sono in tutto e per tutto simili a quanto visto per il comando “Identify Drive”, con l’unica differenza del maggior numero di registri della flash card da inizializzare per questi ultimi, e di conseguenza non verrà trattata tale parte di comandi.

Con questa parte si conclude la sezione dedicata alle inizializzazioni di variabili, hardware ed altro, e si passa alla parte relativa alle istruzioni che si eseguono ripetutamente nella fase run time, dove risiede il cuore dell’algoritmo del sistema: si entra nel corpo del while( ). Prima di entrare nel while( ) però c’è una sezione importantissima di inizializzazioni che vengono ripetute in questo punto perché per la

gestione dei comandi è necessario, all’atto della pressione del tasto Start,

reinizializzare correttamente tutti i vari puntatori che vengono usati nell’algoritmo descritto più avanti, nonché azzerare tutti i campioni presenti nel buffer di SRAM. La ripetizione delle sole inizializzazioni necessarie a questo punto del codice permette di eseguire solo queste, tramite un salto alla label “Init”, e non altre inutili inizializzazioni che sono già state fatte.

(42)

4.2 La fase di Registrazione (Scrittura)

Il corpo del while( ) può essere idealmente diviso, come si intuisce anche nel primo diagramma di flusso presentato in questo capitolo, in due parti: la scrittura e la lettura. In ordine di stesura del codice si sviluppa prima la parte relativa alla registrazione che viene qui di seguito trattata. Dopo una prima parte dedicata alla gestione dei comandi, che verrà ampiamente trattata nel prossimo capitolo, e dopo la sezione relativa all’acquisizione del dato che arriva dalla porta seriale (alla quale arriva dal codec) ed alla assegnazione del dato da mandare al codec in uscita, comincia la parte relativa alla scrittura vera e propria. All’interno di questa medesima sezione ci sono più fasi, come indicato nelle label del seguente diagramma di flusso:

“Flash_card_init” NO SI SI NO “No_Card_Detected_wr” SCRITTURA La flash card è presente?

Sono stati riempiti tutti i settori della flash?

Ovvero: Sectors_Filled==01? Preparazione del dato da inviare:

Smpl_To_Sram = Main_Input

(43)

I blocchi contrassegnati dalla dicitura “Write SRAM Buffer” e “Write Flash Sectors” contengono tutto il blocco di istruzioni che gestiscono la scrittura, appunto, dei campioni nel buffer della SRAM e nei settori della Compact Flash Card. Vediamo di descrivere approfonditamente questi blocchi:

4.2.1 Write SRAM Buffer

Il diagramma di flusso che rappresenta la sequenza di istruzioni presente in questa sezione è il seguente:

SI

NO

“Write Flash Sectors”

“No_Card_Detected_wr”

FINE SCRITTURA

“Write SRAM Buffer”

Viene messo Smpl_ToSram nel Buffer all’indirizzo puntato dal puntatore BufferIN_Waddress e si incrementa il puntatore È stato riempito tutto il Buffer di SRAM? Si riazzera il puntatore BufferIN_WAddress all’indirizzo presente in BufferIN_Start_Address

(44)

SUPERIORE INFERIORE

“Buffer_Flag_OK”

Si vede che il campione viene memorizzato nella SRAM ad indirizzi consecutivi per il fatto che il puntatore al buffer di SRAM viene di volta in volta incrementato. La cosa importante che poi servirà nella sezione dopo è che si prende nota, tramite il flag Flash_BRFlag, della metà di buffer alla quale si sta accedendo in scrittura. Questo serve per capire quale metà buffer si deve trasferire alla flash card mentre nell’altra metà buffer si continua a scrivere i campioni provenienti dal codec. La parte relativa del codice è una diretta implementazione del diagramma di flusso sopra, e quindi non viene riportata.

4.2.2 Write Flash Sectors

Il diagramma di flusso che rappresenta la sequenza di istruzioni presente in questa sezione è il seguente:

Il puntatore BufferIN_Waddress è ad un indirizzo superiore o inferiore rispetto a metà Buffer di

SRAM?

Flash_BRFlag = 0 Flash_BRFlag = 1

“Write Flash Sectors”

(45)

NO SI SI NO “Continue_Write_Sector” SI “No_Card_Detected_wr” NO SI NO NO “Azzera_Out” “No_Card_Detected_wr” SI

Si setta accesso in flash a 16 b

Se il comando di scrittura è già stato inoltrato alla flash card, è stato attivato il

DRQ per iniziare la transazione di dati richiesta? Ovvero: Flag_FrfWite == 0?

È già stata effettuata una prima scrittura dei settori della flash card? Ovvero: First_Write_of_Sector == 1?

Si deve sempre aspettare ad inoltrare il comando di scrittura nella flash card perché non si è ancora finito di riempire la

metà Buffer di SRAM che si stava riempiendo? Ovvero: Flash_BRFlag == Sector_Ok_Write?

C’è stato un errore nella flash card?

La flash è pronta per accettare comandi?

Si inoltra il comando “Write Sector(s)” alla flash card

Si manda alto il piedino CARD_RST

che va direttamente al reset hardware

della flash, poi si rilascia

(46)

“Repeat_Wait_Dreq_wr” SI NO SI NO “No_Card_Detected_wr” NO SI NO SI C’è stato un errore nella flash card?

La flash è pronta e ha attivato il DRQ per fare il trasferimento dei

dati richiesti?

Flag_FRfWrite = 1 Flag_FRfWrite = 0

Se quando si stava scrivendo in SRAM si usava una certa metà del Buffer, l’altra metà viene

usata per prelevare i dati da mandare in flash. Ovvero:

Flash_BRFlag == 1?

Si deve ancora cominciare a trasferire una delle due metà di buffer di SRAM? Se si, si va a scegliere la metà Buffer di SRAM da scrivere nella flash card.

Ovvero:

Num_Sec_Count_Reg == Num_Sectors_To_Access?

Il puntatore BufferIN_RAddress viene

messo a metà BufferIN di SRAM

Il puntatore BufferIN_RAddress viene

messo all’inizio del BufferIN di SRAM

(47)

SI “Flash_Sectors_Filled” NO “Continue_Write_of_Sector” NO “No_Card_Detected_wr” SI

Si incrementa il puntatore che scorre i settori della flash del numero di settori interessato nel trasferimento in atto. Ovvero: LBA_0to15(nuovo) = LBA_0to15(vecchio) + Sec_Count_Reg

Sono stati scritti tutti i settori della flash card? Ovvero LBA_0to15 == Numero totale di

settori della flash card?

Aggiornamento del flag che indica che il comando di scrittura è già stato inoltrato almeno una volta: First_Write_of_Sector = 1

Vengono prelevati 4 campioni a 16 b dalla SRAM e vengono copiati in 4 variabili temporali per essere mandati alla flash card

I quattro campioni precedentemente detti vengono scritti nella flash card Aggiornamento del Flag

Sectors_Filled = 1 in quanto sono stati riempiti

tutti i settori della flash card

“No_Card_Detected_wr”

Il contatore

“Sector_Add_Counter”, che conta i dati mandati alla Flash card, ha raggiunto le dimensioni del buffer

interno alla flash?

Aggiornamento di flag e puntatori: First_Write_of_Sector = 0

Sector_Add_Counter = 0

(48)

NO

SI

“No_Card_Detected_wr”

“Init” “END_OF_PROCESSES”

Si vede dal diagramma di flusso che la gestione è piuttosto complicata, perché la flash È finito il trasferimento di tutti i settori

che si volevano trasferire con un solo accesso? Ovvero:

Num_Sec_Count_Reg(nuovo) == 0 ?

Viene ripristinato il registro che dice quanti settori devono essere trasferiti per il successivo accesso:

Num_Sec_Count_Reg = Num_Sectors_to_Access

Impostazione del flag che dice quando si può ricominciare il trasferimento: Sector_Ok_Write = Flash_BRFlag

“No_Card_Detected_wr”

Si ha prima la parte relativa alla gestione Comandi, poi si ha l’I/O per sentire quello che si sta registrando, tramite l’assegnazione:

Main_Output = Smpl_ToSram

Poi si testa il campione per vedere a quale canale appartiene, secondo il metodo descritto più avanti, e si manda al canale corretto

Sono stati scritti tutti i settori presenti nella flash card? Ovvero: LBA_0to27 == Num_Sectors_of_Flash?

Si manda il sistema in stato di Stop mettendo Flag_start_stop = 0 e poi si

riparte da “Init” dove risiedono inizializzazioni

(49)

più interessante di questa parte di codice è la gestione del buffer di SRAM. Viene data qui di seguito una spiegazione di tale meccanismo, il quale probabilmente è di difficile interpretazione se ci si basa esclusivamente sul diagramma di flusso presentato poc’anzi.

4.2.3 Gestione del Buffer di SRAM in scrittura

Ipotizzando di voler iniziare una scrittura nella flash card i passi che vengono compiuti sono i seguenti:

1) I campioni provenienti dal codec con una cadenza pari al doppio della

frequenza di campionamento (segnale stereo) vengono memorizzati via via nella prima metà del buffer di SRAM, considerandolo idealmente diviso in due parti. Nella flash card viene scritto il contenuto della seconda metà del buffer di SRAM.

2) Una volta superata la prima metà del buffer, i campioni che provengono dal codec continuano ad essere scritti nel buffer di SRAM (si è ormai entrati nella seconda metà del buffer) e contemporaneamente si comincia a trasferire i campioni memorizzati nella prima metà del buffer verso la flash card. In questo caso per ogni campione che viene trasferito dal codec alla SRAM (nella seconda metà in questo momento) dalla prima metà del buffer si trasferiscono quattro campioni verso la flash card. In questo modo la prima metà del buffer di SRAM viene copiata nella flash card in un quarto del tempo che si impiega a riempire la seconda metà del buffer di SRAM con i dati provenienti dal codec. Questo fa sì che ci sia un burst di dati più breve ma con trasferimenti più fitti. Ciò, come già detto nel capitolo dedicato all’hardware, lascia più tempo alla flash per compiere le sue operazioni e

(50)

quindi il READY della flash viene dato prima che si richieda il trasferimento di un altro burst.

3) Quando tutta la prima metà buffer è stata trasferita alla flash card si interrompe il trasferimento verso di essa e si attende che la seconda metà del buffer di SRAM venga completamente riempita dai campioni provenienti dal codec.

4) Quando la seconda metà del buffer è stata riempita il processo di

memorizzazione in SRAM dei dati provenienti dal codec continua di nuovo nella prima metà del buffer, dove ci sono scritti dati che comunque sono già stati trasferiti alla flash card, e quindi possono anche andar perduti. Ora la prima metà e la seconda metà del buffer si scambiano i ruoli: è dalla seconda metà che cominciano ad essere trasferiti campioni verso la flash card, con lo stesso rapporto quattro a uno.

5) Finito il trasferimento di tutta la seconda metà del buffer di SRAM verso la flash card, si chiude il trasferimento verso di lei e si attende che si riempia la prima metà del buffer con i dati provenienti dal codec.

6) Si riparte con il punto 1) con la differenza che la seconda metà del buffer di SRAM ora contiene campioni validi per la flash card.

Ovviamente in una prima fase il buffer di SRAM è azzerato perché altrimenti in riproduzione si sentirebbero dei rumori iniziali dovuti all’invio in flash di dati (casuali) che potrebbero essere presenti in SRAM all’avvio della procedura di registrazione.

(51)

4.3 La fase di Riproduzione (Lettura)

Analizziamo ora il diagramma di flusso della parte di codice relativa alla lettura. È importante notare le molte analogie con la parte relativa alla scrittura, solo che in questo caso si invertono rispettivamente i ruoli del codec e della flash card. Non è più come in scrittura dove i dati entrano nel codec, passano nella SRAM e poi arrivavano alla flash card, ma è il contrario; ovvero i dati partono dalla flash card, passano per la SRAM e arrivano al codec. Questo comporta un diverso posizionamento delle sezioni di codice, perché ora si ha prima la parte relativa all’inoltro del comando di lettura della flash card, e poi la parte che gestisce la SRAM secondo il meccanismo che si può evincere dai diagrammi di flusso e che comunque viene spiegato nel paragrafo successivo.

“Flash_card_init” NO SI SI NO “No_Card_Detected_rd” LETTURA La flash card è presente?

Si deve ancora aspettare a leggere dalla flash?

Ovvero: Sectors_Ok_Read==01? Preparazione del dato da inviare:

Smpl_To_Sram = Main_Input

(52)

Alla stregua del paragrafo precedente, nel diagramma di flusso piuttosto generale sopra riportato sono stati lasciati dei blocchi che contengono tutta una serie di istruzioni per leggere i settori della flash card (“Read Flash Sectors”), scrivere nel buffer di SRAM (“Write SRAM Buffer”) e prelevare i dati dalla SRAM per mandarli verso il codec (“Get data from SRAM”). Analizziamo nel dettaglio ognuno di questi blocchi riportando il diagramma di flusso relativo.

4.3.1 Read Flash Sectors

Il diagramma di flusso che rappresenta la sequenza di istruzioni presente in questa sezione è il seguente:

“Write SRAM Buffer”

“No_Card_Detected_rd”

“Get data from SRAM”

FINE LETTURA

(53)

NO SI SI NO “Continue_Read_Sector” SI “Card_Ok_Read” NO NO “Azzera_Out” SI “No_Card_Detected_rd” “Repeat_Wait_Dreq_rd” SI NO

Se il comando di lettura è già stato inoltrato alla flash card, è stato attivato il DRQ per

iniziare la transazione di dati richiesta? Ovvero: Flag_FrfRead == 0?

È già stata effettuata una prima lettura dei settori della flash card? Ovvero:

First_Read_of_Sector == 1?

C’è stato un errore nella flash card?

Si manda alto il piedino CARD_RST

che va direttamente al reset hardware

della flash, poi si rilascia

La flash è pronta per accettare comandi?

Si inoltra il comando “Read Sector(s)” alla flash card

C’è stato un errore nella flash card?

(54)

NO SI NO “No_Card_Detected_rd” SI “Flash_Sector_All_Read” NO “Continue_Read_Sector”

La flash è pronta e ha attivato il DRQ per fare il trasferimento dei

dati richiesti?

Flag_FRfRead = 1 Flag_FRfRead = 0

Sono stati letti tutti i settori presenti nella flash card? Ovvero:

LBA_0to15(nuovo) == Numero totale di settori nella flash card?

Aggiornamento del puntatore ai settori della flash card, avendo appena inoltrato il comando di lettura:

LBA_0to15(nuovo) = LBA_0to15(vecchio) + Sec_Count_Reg

Aggiorna il flag che indica che il comando di scrittura è già stato inoltrato almeno una volta: First_Read_of_Sector = 1

Vengono prelevati 4 campioni a 16 bit dalla flash card e vengono copiati in 4 variabili temporali per essere

mandati alla SRAM Aggiornamento del Flag

Sectors_AllRead = 1 in quanto sono stati letti tutti

i settori della flash card

“No_Card_Detected_rd”

(55)

4.3.2 Write SRAM Buffer

Il diagramma di flusso che rappresenta la sequenza di istruzioni presente in questa sezione è il seguente: SI NO NO “No_Card_Detected_rd” SI

I quattro campioni precedentemente prelevati dalla flash card vengono inviati alla SRAM

“Write SRAM Buffer”

È stato riempito tutto i l Buffer di SRAM in uscita, ovvero

BufferOUT?

Si ripristina l’indirizzo di partenza del puntatore a

BufferOUT

Il contatore

“Sector_Add_Counter”, che conta i dati prelevati dalla flash card, ha raggiunto le dimensioni del buffer

interno alla flash?

Aggiornamento di flag e puntatori: First_Read_of_Sector = 0

Sector_Add_Counter = 0

(56)

NO

SI

“No_Card_Detected_rd”

4.3.3 Get data from SRAM

Il diagramma di flusso che rappresenta la sequenza di istruzioni presente in questa sezione è il seguente:

È finito il trasferimento di tutti i settori che si volevano trasferire con un solo

accesso? Ovvero: Num_Sec_Count_Reg(nuovo) == 0 ?

Viene ripristinato il registro che dice quanti settori devono essere trasferiti per il successivo accesso:

Num_Sec_Count_Reg = Num_Sectors_to_Access

Si imposta il flag che dice quando si può ricominciare il trasferimento: Sector_Ok_Read = Flash_BRFlag

“No_Card_Detected_rd”

Si ha anche la parte relativa alla gestione Comandi

“Get data from SRAM”

“Get data from SRAM”

Viene prelevato il campione nel Buffer all’indirizzo puntato dal puntatore BufferOUT_RAddress, si copia

nella variabile Smpl_FromSram e si incrementa il puntatore

(57)

SI NO SI NO SI NO “Azzera_Out” SI NO

È stato letto tutto il Buffer di SRAM? Si riazzera il puntatore BufferOUT_RAddress all’indirizzo presente in BufferOUT_Start_Address Aggiornamento di flag: Sector_Ok_Read = 0 Start_Read_Flash = 0 Il puntatore BufferOUT ha raggiunto un indirizzo pari a metà

buffer di SRAM?

Aggiornamento di flag: Sector_Ok_Read = 0

Non si è ancora finito di riempire metà del buffer di SRAM con i dati prelevati dalla flash card, e quindi non si può iniziar e a mandare dati verso il codec? Ovvero:

Start_Read_Flash == 1?

Vengono preparati i dati da inviare al codec per sentire quello che si sta riproducendo, tramite il comando:

Main_Output = Smpl_FromSram

Poi si testa il campione per vedere a quale canale appartiene, secondo il metodo descritto più avanti, e si manda al canale corretto

Sono stati letti tutti i settori presenti nella flash card? Ovvero: LBA_0to27 == Num_Sectors_of_Flash?

Figura

Figura 1: Le operazioni generali
Figura 2: Registro di otto Flip Flop D-Edge Triggered 74LV574
Figura 3: Temporizzazione per un accesso a una memoria esterna
Figura 4: Temporizzazione clock 74LV574
+7

Riferimenti

Documenti correlati

Three-dimensional bond or site percolation theory on a lattice can be interpreted as a gauge the- ory in which the Wilson loops are viewed as counters of topological linking with

By Theorem 30 we know that the portion of the SLD tree actually visited by an interpreter which uses control rules of Prolog and the cut operator is given by the portion of a DT …T DT

L’intensità della fluorescenza del FlouZin3, corrispondente ai livelli di zinco intracellulare, è stata misurata tramite citofluorimetria ed espressa in unità

In line with this, the EURO-HEALTHY PHI was de- signed as a comprehensive tool to enable evidence-based policy-making by (1) allowing for the measuring and monitoring of the

The intracrystalline fraction isolated from Chicoreus and Strombus was tested for closed system behaviour via leaching experiments; for both species, the concentration of free and

Two distinct facies are well represented, one dominated by a black coral population of Leiopathes glaberrima on the western ridge and one by Callogorgia verticillata on the

noscenze delle varie componenti della fruit texture mediante la messa a punto di un nuovo strumento (Texture Analyzer) per la misurazione di questo importante