Platform Designer

Intégration orientée SoC pour FPGA

Tarik Graba

2022-2024

Introduction

Le but de cette séance et de présenter une plateforme d’intégration système orientée FPGA. Ce type d’outil permet de construire un système autour d’un cœur de processeur et utilisant une bibliothèque d’IP. L’outil permet la configuration de la map mémoire, de générer l’interconnect et même de s’interfacer avec un environnement de développement logiciel.

Platform Designer (anciennement Qsys), est un outil fourni par Intel pour cibler les FPGA qu’il produit (anciennement Altera). Il permet de construire un système autour d’un soft core processor, le Nios II ou d’un hard soc ARM pour les FPGA intégrant cette fonction.

Notez qu’il existe des équivalents de cet outil chez d’autres fabricants de circuit FPGA. Nous pouvons citer par exemple, Vivado/Vitis Composer de Xilinx ou Libero SoC de Microsemi. Les concepts abordés durant cette séance se retrouveront aussi sur ces différents outils.

Dans cette présentation nous construirons un système à base du processeur Nios II et nous ciblerons une FPGA de type Cyclone V. De plus, nous écrirons un programme en langage C pour interagir avec les différents blocs du système construit.

Système cible

Le système de base que nous allons construire est représenté par la figure suivante:

Il est composé des éléments suivants:

Nous passerons par les étapes suivantes:

  1. construire le système dans Qsys/Platform Designer
  2. intégrer ce système dans le flot FPGA (Quartus)
  3. écrire un logiciel de test pour tester le fonctionnement des périphériques

Construction du système

Pour simplifier l’intégration au flot de synthèse FPGA, nous démarrerons d’un projet pré-construit. Ce projet contient les informations suivantes:

Une archive de ce projet est disponible ici.

Il est possible d’utiliser directement Platform Designer indépendamment de Quartus. Il faudra dans ce cas, préciser le FPGA cible et ajouter des informations de mapping, puis revenir vers Quartus pour la synthèse.

Une fois l’archive décompressée, vous pouvez ouvrir le projet dans le logiciel Quartus II.

Ouvrez le projet et sélectionnant Open Project... à partir du menu File puis sélectionnez le fichier du projet DE1-SoC.qpf.

Ce projet fait référence à un fichier RTL unique (DE1-SoC.sv) avec la définition des entrées/sorties. Certaines sorties ont été affectées à des valeurs par défaut (par exemple pour éteindre les leds). Le module principal (DE1-SoC) est le module de hiérarchie la plus haute du projet. Par la suite, nous y intégrerons le système généré dans Platform Designer.

Passage à Platform Designer

À partir du menu Tools lancez Platform Designer.

Ceci vous amènera dans l’interface de l’intégrateur et un projet d’intégration vierge semblable à ceci.

C’est le point de départ pour construire le système.

Ce projet contient par défaut un module pour l’horloge et le reset. Il permet juste de connecter des signaux venant du monde extérieur et de resynchroniser le signal de remise à zéro.

Tout système possède au minimum une horloge et une entrée de remiser à zéro, ici, clk et reset. Notez que ces deux signaux apparaissent dans la colonne export, ce qui veut dire qu’ils viendront de l’extérieur du système.

Ajout des éléments su système

Sur le panneau latéral gauche IP Catalog nous avons la liste des IPs disponible. Certaines ont été fournies avec l’outil, d’autres installées par la suite.

Le cœur de processeur

Le premier élément que nous allons ajouter au système sera le processeur, Nios II. Nous le trouverons dans la catégorie Processors and Peripheral/Embedded Processors ou en utilisant la barre de recherche. Pour l’ajouter au système il suffit de double cliquer dessus puis éventuellement de le configurer. Choisissez juste la configuration de base (_resource optimized Nios II/e core).

À la fin de cette étape, vous devriez voir apparaitre le processeur dans le panneau central System Contents comme le montre la figure suivante.

Notez les différentes connexions qui entrent et sortent du processeur.

Vous devriez aussi voir apparaitre des messages d’erreur qui devraient disparaitre une fois les autres éléments du système ajoutés.

La mémoire embarquée

Le second élément que vous devez ajouter est un bloc mémoire. Vous le trouverez dans la catégorie Basic Functions sous le nom On Chip Memory (RAM or ROM)

Cette IP configurable permet d’utiliser les blocs mémoire embarqués du FPGA. Nous pouvons préciser le type de ressources à utiliser, le comportement désiré (read only ou read/write) ainsi que la taille désirée. Le contenu de cette mémoire peut aussi être initialisé à partir d’un fichier (contenant des données ou un programme) qui sera ajouté au fichier de programmation du FPGA (bitstream).

Modifiez la configuration de base pour avoir une taille mémoire de 20Ko comme le montre la figure suivante.

Ceci permettra d’utiliser la fonction printf de la bibliothèque C fournie avec la chaine de compilation.

Les GPIOs

En suite nous ajouterons deux périphériques de type Parallel I/O (PIO) pour les leds et les interrupteurs (switches).

Le premier doit être configuré comme sortie (output) sur une largeur de 10 bits, comme le montre la figure suivante:

Le second doit être configuré comme entrée (inputs) sur une largeur de 10 bits aussi.

Pour la lisibilité de votre projet, renommez les différents module en utilisant des noms explicites.

Par la suite, les signaux sortant de ces modules seront exportés du projet pour être connectés aux entrées/sortie du module principal.

La JTAG UART

En fin, le dernier élément à ajouter est une JTAG UART pour qu’on puisse communiquer avec le système par l’intermédiaire de la liaison USB et interfacer le debugger.

Ici conservez la configuration par défaut.

Interconnecter les éléments

La matrice de connexion est visible dans la colonne Connections de la fenêtre principale. Les points blancs représentent les connexions possibles. Pour activer une connexion, il suffit de cliquer sur ces points. Une fois active, le point symbolisant la connexion devient noir.

L’outil ne vous montre que les connexions entre des interfaces compatibles. Il vous interdira par exemple de connecter une sortie d’horloge sur une entrée de reset.

Voici les connexions qu’il faudra ajouter:

Vous devriez à la fin obtenir un système semblable à celui de la figure suivante.

Remarques

  1. Pour changer le nom d’une instance, sélectionnez-la puis faites <CTRL-R>.
  2. Le Nios-II possède 32 entrées d’interruption (de 0 à 31). Vous pouvez changer l’index de l’interruption dans la colonne IRQ (dans la figure, nous avons choisi la position 5 pour l’interruption de l’uart de façon arbitraire).

Affectation des adresses de base

La colonne Base vous indique les adresses de base de chaque périphérique. Par défaut, quand les instances sont créées, leur adresse de base est 0.

Notez aussi la colonne End qui représente la fin de la plage d’adresse de chaque périphérique. Cette information est obtenue à partir des fichiers décrivant l’IP.

Pour cet exemple simple, il n’y a pas de contrainte particulière sur les adresses de base à attribuer. Nous allons juste choisir de faire commencer la zone réservée à la mémoire à l’adresse 0. Vous pouvez verouiller cette valeur (en cliquant sur le petit cadenas).

Pour les autres périphériques, plutôt que d’affecter manuellement les différentes adresses, nous allons laisser l’outil le faire. Pour cela, choisissez Assign Base Addresses du menu System.

Une fois toutes les adresses de base assignées, l’architecture de votre système devrait être similaire à celui de la figure suivante.

Prennez le temps de noter les différentes adresses, nous en aurons besoin pour écrire le code de test logiciel.

Definition des adresses de reset et d’exceptions processeur

Maintenant que les différentes connexions et adresses ont été définies, nous pouvons revenir sur la configuration du processeur pour définir l’adresse du code de reset ainsi que du handler d’interruption. Ces adresses correspondent, respectivement, à l’adresse de la première instruction exécutée après une remise à zéro (ou un démarrage) ainsi qu’à l’adresse du handler pour les interruptions et exceptions.

Bien que le processeur supporte plusieurs interruptions externes et exceptions, par défaut, celles-ci ne sont pas vectorisées. Toutes les exceptions déclenchent le même code. La détermination de l’origine de l’interruption et le choix du traitement associé se fait de façon logicielle.

Pour revenir dans l’interface de configuration du processeur, il suffit de double cliquer sur la ligne correspondante. Dans l’onglet Vectors vous pouvez soit indiquer une adresse numériquement ou choisir un des périphériques connectés à l’interface instruction_master. La seconde option a l’avantage de vous garantir que l’adresse restera cohérente même si vous changer la map d’adresses.

Ici, choisissez la mémoire embarquée et gardez les offsets par défaut.

Exporter les entrées/sorties

Les entrées/sorties connectées aux contrôleurs de GPIO doivent être connectées aux entrées/sorties adéquates du module RTL principal. Pour cela, il faut demander à Platform Designer de les exporter.

Ceci ce fait dans l’interface graphique en double cliquant sur la ligne correspondante de la colonne Export. Vous pouvez ici aussi modifier le nom, pour qu’il soit plus explicite. Par exemple, dans la figure suivante, nous avons choisi leds et sw.

Sauvegarde et génération du code RTL du système

La dernière étape est de générer l’ensemble des fichiers RTL. Cette version de Platform Designer peut générer du Verilog ou VHDL correspondants à notre système.

Sauvegardez le projet en cliquant sur Save dans le menu File. Nommez le projet, par exemple, cpu_system.

Puis générez le RTL en sélectionnant Generate HDL du menu Generate. Les fichiers seront générés dans un sous dossier du répertoire dans lequel le projet a été créé.

Notez que Platform Designer peut être lancer sans interface graphique. Il inclut un interpréteur TCL et tout ce que nous venons de faire dans l’interface graphique peut être exprimé sous la forme d’un script. Pour les curieux, voir Export System as Platform Designer script (.tcl).

Intégration dans le projet Quartus et programmation du FPGA

Vous pouvez quitter Platform Designer pour revenir dans Quartus.

Vous pouvez ignorer le message vous indiquant comment intégrer le système dans le projet.

Dans Quartus, sélectionner Add/Remove files in project… à partir du menu Project. Cliquez sur l’icone pour ouvrir un explorateur et sélectionnez le fichier .qsys correspondant à votre système.

Une fois fait, vous pouvez modifier le fichier SystemVerilog du projet pour ajouter le système. Platform Designer vous a aussi généré un fichier _inst.v (par exemple cpu_system_inst.v) contenant un exemple d’instanciation.

Connectez l’entrée d’horloge au à l’entrée clock_50 du module principal, et le reset sur l’un des boutons poussoirs, key[0] par exemple. Connectez aussi les interrupteurs et leds aux entrées/sorties correspondants aux GPIOs.

Voici un exemple d’instanciation:

//////////////////////////////////////////////////////////////////////
// Nios-II System
//////////////////////////////////////////////////////////////////////

    cpu_system u0 (
        .clk_clk       ( clock_50 ) ,
        .reset_reset_n ( key[0]   ) ,
        .sw_export     ( sw       ) ,
        .leds_export   ( ledr     )
    );

Note

Une fois l’instance ajoutée, vous pouvez compiler (synthèse, placement routage et génération du fichier de programmation) dans Quartus. Puis, vous pouvez lancer le programmeur pour programmer le FPGA de la carte.

Test sur la maquette

Intel/Altera fourni une chaine de compilation pour le processeur Nios-II basée sur les outils GNU (gcc, binutils,…).

L’intégration dans un environnement de développement logiciel est aussi prévu:

De plus, des bibliothèques d’abstraction sont fournies pour les différents périphériques disponibles dans Platform Designer.

Pour ce projet, nous nous limiterons à un simple environnement familier et utilisant gnu make et gdb (en ligne de commande ou à travers votre éditeur préféré).

Environnement logiciel

Intel propose des IDE pour le développement logiciel pour Nios-II.

Ces deux outils (bien qu’ayant des fonctionnalités différentes) savent générer un projet logiciel à partir de la description du projet de Platform Designer. Pour ce cours, nous allons nous restreindre à des programmes simples et nous n’utiliserons pas d’IDE (juste un makefile).

Récupérer l’environnement logiciel disponible ici.

Vous y trouverez les sources d’un projet exemple pour une plateforme pré-définie. Le projet contient:

Attention cet exemple est prévu pour une plateforme matérielle différente!

Test du programme d’exemple:

Pour cela, il faudra:

  1. reconfigurer le FPGA,
make fpga-conf
  1. attacher un serveur gdb,
make gdb-server
  1. attacher un terminal (pour la jtag-uart),
make terminal
  1. lancer une session GDB.
make debug

Par défaut, le script d’inialisation de GDB, charge le programme et positionne un point d’arrêt sur la fonction main.

Une session de débug devrait ressembler à ceci:

Test du système que vous avez conçu

Pour pouvoir tester les périphériques que nous avons ajouté au système précédemment conçu, voici un programme de test simple qui affiche en continu sur les leds de la carte l’état des interrupteurs.

// define macros to access to the data registers of
// the LEDs and SWs PIOs
#define LEDS  (*(volatile unsigned int*) 0x00009010)
#define SW    (*(volatile unsigned int*) 0x00009000)

int main()
{
  while(1)
    LEDS = SW;

}

Modification du Makefile

Bien qu’Intel/Altera, prévoit, pour ces outils de développement logiciel, la possibilité récupérer automatiquement des informations à partir des fichiers générés par Platform Designer, pour ce TP, nous allons faire les modifications manuellement.

Donc, dans le makefile, modifiez les points suivants:

Remplacez, finalement, le programme principal par votre programme de test.