La crescente capacità logica dei dispostivi programmabili consente oggi di realizzare complessi System-On-Chip che includono le principali funzionalità di sistema in un unico dispositivo. Tuttavia, unitamente alla complessità del sistema, crescono le problematiche legate alla fase di verifica del progetto che tendono ad incidere negativamente sul time-to-market del prodotto. Identify è un software per il debug in circuit di FPGA sviluppato tempo fa da Synplicity, leader nell’ambito dei tool EDA per logiche programmabili ed ASIC.
Introduzione
La figura 1a mostra il flusso di processo che viene tradizionalmente seguito nel progetto di sistemi complessi mediante logiche programmabili. Il codice sorgente viene verificato funzionalmente creando opportuni test-bench che cercano di coprire la maggior parte dei casi reali. I tool di sintesi e place&route traducono quindi il codice in un file di programmazione per il dispositivo. Per favorire il debug nelle situazioni reali, un numero limitato di pin della FPGA vengono riservati dal progettista per monitorare mediante un analizzatore di stati logici segnali di controllo interni.
Tuttavia, in seguito alla crescente complessità dei progetti, è divenuto piuttosto difficile prevedere tutti i possibili casi in simulazione; allo stesso modo, il numero ridotto di segnali che è possibile monitorare non sempre consente di individuare correttamente i malfunzionamenti del circuito. Si è quindi avvertita sempre più insistentemente l’esigenza di tool per il debug in circuit sulla falsariga degli ambienti di emulazione/debugger diffusamente utilizzati per la validazione degli applicativi software.
La figura 1b mostra come il flusso di processo nel progetto di logiche programmabili si modifica utilizzando Synplicity Identify. Prima di essere sintetizzato, il progetto viene ‘strumentato’; in altri termini, direttamente nel codice sorgente, come mostrato in seguito, vengono definiti i segnali che si intende monitorare nella successiva fase di debug. Il tool, in maniere automatica, crea una struttura logica che permette di acquisire nel tempo il valore di tali segnali e definire complesse condizioni di trigger. Tale logica accessoria è del tutto equivalente ad un analizzatore di stati logici integrato nel dispositivo e quindi in grado di accedere virtualmente a tutti i segnali di controllo e stato interni. Non è necessario prevedere a priori test-point nel circuito stampato come in un approccio classico; internamente alla FPGA viene creata una memoria per i segnali da monitorare. La disponibilità di risorse per tale scopo diventa quindi l’unico vincolo alla quantità di campioni ed al numero di segnali che è possibile acquisire. In fase di debug, da remoto, è possibile controllare le condizioni di trigger in corrispondenza delle quali acquisire il buffer di memoria. In questo modo si è in grado di eseguire la simulazione funzionale, che nell’approccio classico precede la fase di sintesi, direttamente sul target reale riducendo significativamente i tempi di prototyping e debug. Gli elementi principali che sono inclusi nell’ambiente Identify sono:
- l’IICE (Intelligent In-Circuit Emulator), la logica accessoria integrata nel progetto logico del dispositivo per il monitoring dei segnali
- l’Instrumentor, il tool per l’integrazione automatica dell’IICE
- il Debugger, il software per il controllo da remoto dell’IICE, l’acquisizione dei segnali in corrispondenza delle condizioni di trigger ed il display di questi come forme d’onda
I paragrafi successivi riportano una descrizione più dettagliata dei tre elementi.
IICE
L’IICE costituisce, come accennato in precedenza, l’analizzatore di stati logici che viene integrato all’interno della FPGA. Comprende principalmente i seguenti blocchi funzionali:
- il JTAG controller, per la comunicazione con il debugger via porta JTAG;
- il sampler, per l’acquisizione dei test point;
- il controller, per la gestione delle condizioni di trigger.
Numerosi parametri possono essere configurati per i diversi blocchi. Nel caso ad esempio della comunicazione JTAG, è possibile specificare se si intende utilizzare un eventuale controller builtin disponibile nel dispositivo od il Tap-Controller di Synplify, sintetizzato utilizzando logica utente. In questo caso, qualora non sia disponibile una risorsa dedicata per la distribuzione del clock JTAG, è possibile specificare la creazione di una struttura hardware master/slave instrinsecamente skew-resistant. Nel caso del sampler, invece, sono configurabili la dimensione del buffer di memoria utilizzato per l’acquisizione dei segnali di test, il tipo di risorsa hardware da utilizzare (flip-flop o RAM embedded qualora disponibili), il clock di campionamento ed il relativo fronte attivo. Nel caso si intenda monitorare segnali appartenenti a domini di clock distinti, è possibile istanziare nel progetto unità IICE multiple. Sono inoltre attivabili due interessanti modalità di campionamento dei segnali:
- qualified sampling: in questo caso viene acquisito un singolo campione in corrispondenza di ogni occorrenza del trigger consentendo di osservare l’evoluzione del sistema su un periodo piuttosto lungo di tempo;
- always-armed triggering: il buffer di memoria viene salvato in corrispondenza del trigger ma il sistema resta armato fino al successivo trigger od interruzione. Tale modalità è tipicamente utilizzata nell’analisi di sistemi che utilizzino un pattern di trigger costante (ad esempio un ciclo di bus) richiedendo casualmente l’acquisizione del buffer di memoria.
Allo stesso modo sono disponibili tre diverse modalità di trigger nella configurazione del controller:
- simple triggering, basato su singoli watchpoints o breakpoints. I primi rappresentano i segnali che vengono monitorati ed in base al cui valore è possibile impostare le condizioni di trigger. I breakpoint sono invece istruzioni condizionali che provocano un evento di trigger quando vengono eseguite.
- complex counter triggering, per il quale sono definite le seguenti modalità:
- events mode: l’acquisizione del buffer di memoria viene richiesta in corrispondenza
dell’ n-mo trigger; - cycles mode: l’acquisizione del buffer di memoria viene richiesta n cicli di clock dopo l’occorrenza del trigger;
- watchdog mode: l’acquisizione del buffer di memoria viene richiesta se il trigger non si verifica entro n cicli di clock dal momento in cui il sistema è armato;
- pulsewidth mode: l’acquisizione del buffer di memoria viene richiesta se la condizione di trigger è verificata per n cicli di clock consecutivi.
- events mode: l’acquisizione del buffer di memoria viene richiesta in corrispondenza
- state machine triggering, per la creazione di sequenze predefinite di eventi di trigger al termine delle quali richiedere l’acquisizione del buffer di memoria.
Identify Instrumentor
L’Instrumentor (figura 2) è l’ambiente che consente la creazione ed inserzione automatica dell’IICE nel progetto di riferimento.
Come osservato in precedenza, il tool opera direttamente sul codice sorgente per definire watchpoints e breakpoints. Come mostrato in figura 2, visualizzando infatti il codice all’interno di Identify, vengono automaticamente evidenziati i segnali che possono essere associati a watchpoints (sottolineati e colorati in blu) e le istruzioni che possono costituire breakpoints (indicate da un bottone grigio a margine).
Cliccando direttamente sulle icone di riferimento è possibile abilitare o escludere la corrispondente condizione. In questo modo l’intero processo di ‘strumentazione’ del progetto viene reso estremamente intuitivo ed immediato. Per rendere ancora più agevole l’intero processo, inoltre, la schermata principale dell’Instrumentor, come mostrato in figura 2, presenta nella parte sinistra una finestra di lavoro che ricostruisce la gerarchia dl progetto. Questa evidenzia, nel caso ad esempio di codice in linguaggio VHDL, l’entità top-level, le architetture disponibili, le istruzioni di istanziazione di componenti, di definizione di processo, di controllo di flusso, i sottoprogrammi ed i blocchi. È possibile elencare in una finestra di popup tutti i segnali e le istruzione che possono costituire watchpoints e breakpoints evidenziando quelli già strumentati. In base ai watchpoints e breakpoints definiti, l’Instrumentor modifica il codice sorgente per l’inserzione dell’IICE e la connessione dei relativi probe. Tale codice modificato può quindi essere sintetizzato come nell’approccio classico; in particolare vengono generati script TCL per l’esecuzione dei tool di sintesi più diffusi tra i quali Synplify o Leonardo. Una console riporta in formato testuale tutti i comandi che sono stati eseguiti; è possibile salvare l’intera lista come script come TCL da eseguire in successive iterazioni.
Identify Debugger
La figura 3 mostra la finestra di strumentazione di Identify Debugger, del tutto simile alla schermata analoga dell’Instrumentor.
Diversamente da questa, tuttavia, vengono evidenziati ora nel codice sorgente soltanto le istruzioni che sono state definite come breakpoints ed i segnali scelti come watchpoints. Cliccando sul relativo pulsante verde di riferimento, ad esempio, è possibile abilitare come trigger i breakpoints; cliccando invece in corrispondenza dei segnali si apre la finestra che consente di definire il valore (o la transizione di valori) rispetto a cui creare il trigger. Nel caso di complex counter trigger, inoltre, è possibile selezionare la modalità relativa e definire il valore del contatore. Nel caso, invece, di utilizzo di una configurazione di trigger basata su macchina a stati, è abilitata all’interno del Debugger una finestra (figura 4) con semplice interfaccia grafica. Sono elencati tutti gli stati previsti in fase di strumentazione ed è quindi possibile definire per ognuno di essi la relativa condizione di trigger oltre alle transizioni tra stati.
L’insieme di watchpoints e breakpoints abilitati o, più in generale, la configurazione di trigger impostata costituisce quella che viene chiamata ‘attivazione’ del sistema. Il Debugger consente di salvare sottoforma di script e richiamare successivamente una qualunque attivazione. Il comando di run permette di armare il sistema. Vengono comunicate all’IICE le condizioni di trigger che sono state configurate; il Debugger resta quindi in attesa di riceve i campioni memorizzati nel buffer, qualora una di queste si verifichi. Una volta acquisito, l’intero buffer può successivamente essere scorso in avanti o indietro a partire da un suo qualunque campione; ad ogni passo, il corrispondente valore dei watchpoints viene aggiornato direttamente nella finestra che mostra il codice sorgente consentendo in questo modo di seguire l’evoluzione del sistema come sequenza di istruzioni eseguite. In certi casi, tale approccio risulta più intuitivo e permette un debug più efficace. Diversamente, è possibile esportare l’intero buffer in un formato compatibile con i più diffusi viewer di forme d’onda.