# Partie 1 : FPGA, HDL, simulation, synthétisation ? 
Si vous êtes là, c'est que vous avez probablement entendu parler de FPGA quelque part et que vous souhaitez en savoir plus sur leur fonctionnement, éventuellement expérimenter sur eux. Vous avez donc besoin de quelques notions théoriques pour avancer.

Ça tombe bien, c'est l'objet de cette première partie.

__Table des matières__
- [Partie 1 : FPGA, HDL, simulation, synthétisation ?](#partie-1--fpga-hdl-simulation-synthétisation-)
  - [FPGA et Microcontrôleur](#fpga-et-microcontrôleur)
    - [Microcontrôleur](#microcontrôleur)
    - [FPGA](#fpga)
  - [Description, Simulation et Synthèse](#description-simulation-et-synthèse)
    - [Description d'un composant](#description-dun-composant)
    - [Simulation et banc de test](#simulation-et-banc-de-test)
    - [Synthétisation de l'entité et téléversement sur la carte](#synthétisation-de-lentité-et-téléversement-sur-la-carte)
  - [Exercices](#exercices)


## FPGA et Microcontrôleur

### Microcontrôleur
Vous êtes probablement familier avec le concept de **microcontrôleur** et leur principe de fonctionnement. De manière générale, il est composé d'une unité de calcul, d'entrées et de sortie, de mémoire volatile et non volatile.

Pour le faire fonctionner, vous développer un microprogramme qui une fois compilé est téléchargé dans la mémoire non volatile du microcontrôleur. 

Si vous avez déjà utilisé une carte Arduino, vous connaissez probablement le code C suivant : 
```c
/* 
 Source :https://www.arduino.cc/en/Tutorial/BuiltInExamples/Blink
*/

void setup() {
  // initialize digital pin LED_BUILTIN as an output.
  pinMode(LED_BUILTIN, OUTPUT);
}

// the loop function runs over and over again forever
void loop() {
  digitalWrite(LED_BUILTIN, HIGH);   // turn the LED on (HIGH is the voltage level)
  delay(1000);                       // wait for a second
  digitalWrite(LED_BUILTIN, LOW);    // turn the LED off by making the voltage LOW
  delay(1000);                       // wait for a second
}
```
### FPGA 
Un  **FPGA (Field Programmable Grid Array)** fonctionne quant à lui bien différemment : ce n'est pas un microcontrôleur.

Un FPGA est un composant électronique dit de _circuits logiques programmables_, c'est à dire qu'il est composé d'un assemblage de porte logique (AND, OR, Not, etc ...), de module de routage et de mémoire non volatiles permettant de "câbler" le circuit logique.

Un FPGA n'exécute donc pas de programme comme le ferait un microcontrôleur. Il fonctionne en effet sur un principe de logique combinatoire temps réel (synchrone, asynchrone ou hybride). Les langages permettant de concevoir cette logique sont donc des langages de description matériel et non par des langages de programmation.

Les langages de description matériel, aussi appelés **HDL (Hardware Description Language**),  sont assez nombreux. Les plus couramment utilisés sont le _Verilog_, _SystemC_ et le _VHDL_. Dans notre cas, nous nous intéresserons au dernier.
D'une manière générale, ces langages permettent la description, simulation de fonctionnement et synthétisation de composants matériels.

Un exemple  de VHDL :
```vhdl
--- Source http://esd.cs.ucr.edu/labs/tutorial/fsm.vhd
-----------------------------------------------------
-- VHDL FSM (Finite State Machine) modeling
-- (ESD book Figure 2.7)
-- by Weijun Zhang, 04/2001
--
-- FSM model consists of two concurrent processes
-- state_reg and comb_logic
-- we use case statement to describe the state 
-- transistion. All the inputs and signals are
-- put into the process sensitive list.  
-----------------------------------------------------

LIBRARY ieee;
USE ieee.std_logic_1164.ALL;

-----------------------------------------------------

ENTITY seq_design IS
	PORT (
		a : IN STD_LOGIC;
		clock : IN STD_LOGIC;
		reset : IN STD_LOGIC;
		x : OUT STD_LOGIC
	);
END seq_design;

-----------------------------------------------------

ARCHITECTURE FSM OF seq_design IS

	-- define the states of FSM model

	TYPE state_type IS (S0, S1, S2, S3);
	SIGNAL next_state, current_state : state_type;

BEGIN

	-- cocurrent process#1: state registers
	state_reg : PROCESS (clock, reset)
	BEGIN

		IF (reset = '1') THEN
			current_state <= S0;
		ELSIF (clock'event AND clock = '1') THEN
			current_state <= next_state;
		END IF;

	END PROCESS;

	-- cocurrent process#2: combinational logic
	comb_logic : PROCESS (current_state, a
		)
	BEGIN

		-- use case statement to show the 
		-- state transistion

		CASE current_state IS

			WHEN S0 => x <= '0';
				IF a
					= '0' THEN
					next_state <= S0;
				ELSIF a
					= '1' THEN
					next_state <= S1;
				END IF;

			WHEN S1 => x <= '0';
				IF a
					= '0' THEN
					next_state <= S1;
				ELSIF a
					= '1' THEN
					next_state <= S2;
				END IF;

			WHEN S2 => x <= '0';
				IF a
					= '0' THEN
					next_state <= S2;
				ELSIF a
					= '1' THEN
					next_state <= S3;
				END IF;

			WHEN S3 => x <= '1';
				IF a
					= '0' THEN
					next_state <= S3;
				ELSIF a
					= '1' THEN
					next_state <= S0;
				END IF;

			WHEN OTHERS =>
				x <= '0';
				next_state <= S0;

		END CASE;

	END PROCESS;

END FSM;

```

## Description, Simulation et Synthèse
Il existe trois étapes importantes dans la création d'architecture matérielle :
- la description du fonctionnement d'un composant logique
- la simulation de ce composant et la validation de son fonctionnement
- la synthétisation du composant pour l'architecture du FPGA

### Description d'un composant
Comme pour les composants physiques, les composants logiques décrits en VHDL sont constitués d'entrées, de sorties et d'une logique effectuant un traitement. On pourrait le comparer par analogie à une fonction dans un programme.

En se basant sur l'extrait de code VHDL ci-dessus, on peut analyser comment est représenté un composant en VHDL :

1. **Déclaration des bibliothèques :**
   Les langages HDL étant normées et étant conçus sur une base d'éléments logiques pré-implémentés, il est nécessaire de déclarer quelques bibliothèques standards par défaut.
   ```vhdl
   LIBRARY ieee;
   USE ieee.std_logic_1164.ALL;
   ```
2. **Déclaration d'une entité :**
   En VHDL, un composant est appelé une _entité_ et dispose de ports d'entrées et de sorties.
   Chaque port défini un mode de fonctionnement (in / out / etc ...) et son type, ici std_logic (booléen).
   ```vhdl
   ENTITY seq_design IS
       PORT (
	       a : IN STD_LOGIC;
		   clock : IN STD_LOGIC;
		   reset : IN STD_LOGIC;
		   x : OUT STD_LOGIC
	   );
   END seq_design;
   ```


3. **Déclaration d'une architecture :**
   Une entité doit au moins référencer une architecture logique.
   Elle décrit les opérations logiques qui permettent de modifier les états des sorties de l'entité en fonction des états des entrées.
   ```vhdl
   ARCHITECTURE FSM OF seq_design IS
       -- define the states of FSM model
	   TYPE state_type IS (S0, S1, S2, S3);
	   SIGNAL next_state, current_state : state_type;
	   
   BEGIN
       -- cocurrent process#1: state registers
	   state_reg : PROCESS (clock, reset)
	   BEGIN    
	       IF (reset = '1') THEN
		       current_state <= S0;
		   ELSIF (clock'event AND clock = '1') THEN
		       current_state <= next_state;
		   END IF;
	   END PROCESS;

	   -- cocurrent process#2: combinational logic
	   comb_logic : PROCESS (current_state, a)
	   BEGIN    
		   -- use case statement to show the 
		   -- state transistion
		   CASE current_state IS
		       WHEN S0 => x <= '0';
			       IF a = '0' THEN
					   next_state <= S0;
				   ELSIF a = '1' THEN
					   next_state <= S1;
				   END IF;

		   -- some more code --

		   END CASE;
       END PROCESS;
   END FSM;

   ```
On remarque alors deux blocs de code distinct : un premier comportant une définition de _type_ et _signal_, un second composé d'un _process_. 

Le premier bloc correspond à ce que l'on pourrait associer à une définition de type de donnée (ici une énumération) et de variable en développement logiciel.  
Le second bloc s’apparente quant à lui à la fonction _loop()_ d'un programme Arduino : il s'agit d'un processus, c'est à dire une suite d'instructions séquentiel synchronisé sur une horloge.

⚠️ ATTENTION : le fonctionnement d'un processus est plus compliqué qu'une simple boucle infini : nous reviendrons sur ce sujet plus tard.

Vous avez donc maintenant une petite idée de comment est structuré une entité (composant) en VHDL. Bien entendu l'exemple est relativement simple et n'implémente pas d'autres composants, mais pas de stress car cela va vite se complexifier

### Simulation et banc de test
Dans le monde de l'embarqué, nous aimons bien simuler le fonctionnement de systèmes durant leur phase de conception. Cela permet de s'assurer qu'ils réagissent correctement dans différents scénarios de fonctionnement et que leur comportement ne dévie pas des spécifications.

Je vous le donne dans le mille : il en va même pour les entités en VHDL.

Pour simuler une entité, nous avons besoin de trois éléments : 
- La description de l'entité, soit le code VHDL
- Un banc de test, ensemble d'instructions indiquant quels signaux appliquer sur les entréss de l'entité et les valeurs attendu en sortie.  
  Il est aussi écrit en VHDL mais n'est pas du VHDL RTL (Real Time Logic) et donc non synthétisable.
- Un logiciel capable de simuler notre couple entité et banc de test

Dans notre cas, nous utiliserons le logiciel [Questa*-Intel® FPGA Starter Edition](https://www.intel.fr/content/www/fr/fr/software/programmable/quartus-prime/questa-edition.html) qui est intégré avec la suite logicielle [Intel® Quartus® Prime Lite Edition](https://www.intel.fr/content/www/fr/fr/products/details/fpga/development-tools/quartus-prime.html).

Il ne nous manque plus qu'à rédiger le banc de test afin de pouvoir simuler le fonctionnement de notre entité.
```vhdl
--- Source http://esd.cs.ucr.edu/labs/tutorial/fsm.vhd
-----------------------------------------------------------------
-- test bench for FSM (ESD book figure 2.7)
-- by Weijun Zhang, 04/2001
-----------------------------------------------------------------

LIBRARY ieee;
USE ieee.std_logic_arith.ALL;
USE ieee.std_logic_unsigned.ALL;
USE ieee.std_logic_1164.ALL;

ENTITY fsm_tb IS -- entity declaration
END fsm_tb;

-----------------------------------------------------------------

ARCHITECTURE TB OF fsm_tb IS

    SIGNAL T_a : STD_LOGIC;
    SIGNAL T_clock : STD_LOGIC;
    SIGNAL T_reset : STD_LOGIC;
    SIGNAL T_x : STD_LOGIC;

    COMPONENT seq_design
        PORT (
            a : IN STD_LOGIC;
            clock : IN STD_LOGIC;
            reset : IN STD_LOGIC;
            x : OUT STD_LOGIC
        );
    END COMPONENT;

BEGIN

    U_fsm : seq_design PORT MAP(T_a, T_clock, T_reset, T_x);

    PROCESS
    BEGIN
        T_clock <= '1'; -- clock cycle 10 ns
        WAIT FOR 5 ns;
        T_clock <= '0';
        WAIT FOR 5 ns;
    END PROCESS;

    PROCESS

        VARIABLE err_cnt : INTEGER := 0;

    BEGIN

        -- case 1		
        T_reset <= '1';
        WAIT FOR 20 ns;
        ASSERT (T_x = '0') REPORT "Failed Case 1" SEVERITY error;
        IF (T_x /= '0') THEN
            err_cnt := err_cnt + 1;
        END IF;

        -- case 2
        T_reset <= '0';
        T_a <= '0';
        WAIT FOR 20 ns;
        ASSERT (T_x = '0') REPORT "Failed Case 2" SEVERITY error;
        IF (T_x /= '0') THEN
            err_cnt := err_cnt + 1;
        END IF;

        -- case 3
        WAIT FOR 30 ns;
        T_a <= '1';
        WAIT FOR 35 ns;
        ASSERT (T_x = '1') REPORT "Failed Case 3" SEVERITY error;
        IF (T_x /= '1') THEN
            err_cnt := err_cnt + 1;
        END IF;

        -- case 4
        WAIT FOR 70 ns;
        T_reset <= '1';
        WAIT FOR 10 ns;
        ASSERT (T_x = '0') REPORT "Failed Case 4" SEVERITY error;
        IF (T_x /= '0') THEN
            err_cnt := err_cnt + 1;
        END IF;

        -- summary of all the tests	
        IF (err_cnt = 0) THEN
            ASSERT false
            REPORT "Testbench of FSM completely successfully!"
                SEVERITY note;
        ELSE
            ASSERT true
            REPORT "Something wrong, Check again pls!"

                SEVERITY error;
        END IF;

        WAIT;

    END PROCESS;

END TB;

----------------------------------------------------------------------
CONFIGURATION CFG_TB OF fsm_TB IS
    FOR TB
    END FOR;
END CFG_TB;
```

Comme pour la définition de l'entité, on retrouve dans le banc de test une importation de bibliothèque standard ainsi que la déclaration d'une architecture avec divers types et signaux.   
On remarque cependant plusieurs différences :
 - est présent la déclaration d'un composant et de son `port map`
 - est présent des instructions `wait` / `wait for`, `assert`, `report` et `severity` qui sont non synthétisables.
 - sont présents deux process distincts dans la description du comportement.

Analysons donc ces différences :

1. **Déclaration d'un composant et assignation de signaux**:   
   Il est possible d'intégrer un composant existant dans notre architecture. Elle se fait en deux temps dans l'architecture.   
   Nous déclarons en premier lieu notre composant avec ses signaux d'entrée et de sortie interne.
   ```vhdl
   COMPONENT seq_design
       PORT (
           a : IN STD_LOGIC;
           clock : IN STD_LOGIC;
           reset : IN STD_LOGIC;
           x : OUT STD_LOGIC
       );
   END COMPONENT;
   ```
   Dans un second temps, dans la description du comportement du banc de test, nous allons lui assigner des signaux externes : ceux exploités par notre architecture.
   ```vhdl
   U_fsm: seq_design PORT MAP(T_a, T_clock, T_reset, T_x);
   ```
2. **Déclaration des processus**:   
   Cette architecture de banc de test dispose de deux processus distincts : 
   - un premier chargé de générer le signal d'horloge
   - un second chargé d'effectuer les tests successifs sur le composant importé

   _Générateur d'horloge_ :
   ```vhdl
   PROCESS
       BEGIN
           T_clock <= '1'; -- clock cycle 10 ns
           WAIT FOR 5 ns;
           T_clock <= '0';
           WAIT FOR 5 ns;
   END PROCESS;
   ```

   _Test séquentiel du composant_
   ```vhdl
       PROCESS

        VARIABLE err_cnt : INTEGER := 0;

    BEGIN

        -- case 1		
        T_reset <= '1';
        WAIT FOR 20 ns;
        ASSERT (T_x = '0') REPORT "Failed Case 1" SEVERITY error;
        IF (T_x /= '0') THEN
            err_cnt := err_cnt + 1;
        END IF;

        -- some more tests --

        -- summary of all the tests	
        IF (err_cnt = 0) THEN
            ASSERT false
            REPORT "Testbench of FSM completely successfully!"
                SEVERITY note;
        ELSE
            ASSERT true
            REPORT "Something wrong, Check again pls!"

                SEVERITY error;
        END IF;

        WAIT;

    END PROCESS;
   ```
   On remarque qu'il est possible de faire afficher au logiciel de simulation des messages avec des niveaux différents : il est ainsi possible d'automatiser les tests.

   Après lancement de la simulation (voir les exercices) nous obtenons ainsi les résultats suivants :   
   _Waveform post-simulation_   
   ![Waveform post-simulation](images/simu_waves.png)   
   _Sortie dans console_   
   ![Sortie dans console_](images/simu_term.png)

### Synthétisation de l'entité et téléversement sur la carte
Étape cruciale dans le développement sur FPGA, la synthétisation est l'étape de conversion d'une entité décrite en langage HDL vers une représentation binaire (un bitstream) des assemblages de portes logiques à appliquer sur le FPGA.

Cette opération s'effectue en règle générale dans le logiciel de développement intégré du fondeur de la puce FPGA, logiciel souvent soumis à licence. Il existe en effet peut de FPGA dont les logiciels de synthétisation sont libres.   
Dans notre cas, nous utilisons le logiciel [Intel® Quartus® Prime Lite Edition](https://www.intel.fr/content/www/fr/fr/products/details/fpga/development-tools/quartus-prime.html).

Il est nécessaire de configurer plusieurs paramètres pour pouvoir synthétiser une entité sur Quartus :
- Configurer le modèle exact de FPGA
- Définir les entités à synthétiser

 
Une fois ces configurations faites (voir exercices), il sera alors possible d'associer les signaux de l'entité principale aux entrés et sorties physiques du FPGA puis compiler le projet. La dernière étape sera pa programmation du FPGA après la résolution des éventuelles erreurs.

## Exercices
Je vous propose maintenant d'ouvrir la fiche d'exercice afin de pourvoir réaliser les premiers exercices.

Voir [Fiche Exercices](EXERCICES.md)