# Very High Speed Integrated Circuit Hardware Description Language/Micro contrôleur embarqué : le PicoBlaze

Le picoblaze est probablement l'architecture par laquelle on doit commencer pour apprendre à utiliser un processeur softcore dans un FPGA Xilinx. Il a une architecture très simple qui peut facilement s'étendre matériellement.
* Le picoblaze est une architecture avec laquelle on travaille en VHDL (ou verilog) : architecture RTL
* Malheureusement le picoblaze est une architecture qui est liée au constructeur Xilinx
* Il existe une version du picoblaze utilisable avec les autres fabricants écrit en verilog

## Le modèle de programmation du PicoBlaze

Le modèle de programmation est très simple. Le picoBlaze est constitué de 16 registres 8 bits d'utilisation générale notés de s0 à sF. On trouve aussi un compteur programme sur 10 bits, un pointeur de pile (tos = Top Of Stack) sur 31 niveaux et un registre d'état avec 3 bits appelés drapeaux :
* C : retenue (Carry)
* Z : zéro
* I : masque d'interruption
Les deux mémoires, programme et données, sont organisées de la manière suivante:
* La mémoire programme est organisée en 1024 instructions sur 18 bits. L'adressage de la mémoire programme se fait donc sur 10 bits ($2^{10}=1024$).
* La mémoire donnée est organisée en 64 octets. L'adressage se fait donc sur 6 bits ($2^{6}=64$)
Par comparaison, les premiers microprocesseurs 8 bits des années 1970 avaient un espace d'adressage sur 16 bits. Cet espace mélangeait données et programme. Ici on a une séparation, une telle architecture est appelée Harvard.

## Quelques instructions
### Les modes d'adressage

Le PicoBlaze connaît cinq modes d'adressage.
* Adressage registre : c’est l'adressage le plus simple, il ne peut concerner que des registres.

#### Instruction en adressage registre

    Op-code (Hexa)instructionopérande(s)commentaire 0 10 10loads0,s1;charger s1 dans s0
Remarquez la taille de l’Opcode : 18 bits. Ces 18 bits sont composés de 5 caractères Hexadécimaux de 4 bits. Ils représentent donc 20 bits. Pour se contenter de 18 bits, le caractère le plus à gauche ne peut dépasser la valeur 3.
* Adressage immédiat (Immediate) : l’opérande correspondant se trouve directement dans le programme derrière le code de l’instruction et un premier opérande. Il se fait sur 8 bits.
On peut remarquer que les nombres de l'adressage immédiat (deuxième opérande) sont systématiquement écrits en hexadécimal.

#### Instruction en adressage immédiat

    instructionopérande 1opérande 2Op-code LOADsXaaaa aaaa00 0000 xxxx aaaa aaaa ADDsXaaaa aaaa01 0010 xxxx aaaa aaaa
sX désigne un registre dont le numéro X peut varier de 0 à 15 qui est donc codé sur 4 bits "xxxx".
* Immédiat 10 bits : l'adresse est donnée sur 10 bits. L'instruction JUMP utilise ce mode d'adressage parce que l'adresse fournie est une adresse absolue de programme.

#### Instruction en adressage immédiat 10 bits

    instructionopérande 1opérande 2Op-code JUMPaa aaaa aaaa11 0100 00aa aaaa aaaa
* Direct 6 bits : l'adresse de la mémoire se trouve dans le dernier octet du code de l'instruction (sur les 6 bits de poids faible). Les instructions STORE et FETCH fonctionnent avec ce mode d'adressage et permettent d'adresser 64 octets (seulement) de la mémoire.
À noter encore les opcodes sur 18 bits (5 caractères hexadécimaux).

#### Instruction en adressage direct 6 bits (RAM)

    instructionopérande 1opérande 2Op-code FETCHsXaa aaaa00 0110 xxxx 00aa aaaa
L'instruction FETCH est une instruction qui permet de lire une donnée en mémoire.
* Adressage indexé : l'adresse de la mémoire se trouve dans un registre. Nous donnons ci-dessous le format de cette instruction.

#### Instruction en adressage indexé par un registre (RAM)

    instructionopérande 1opérande 2Op-code FETCHsX(sY)00 0111 xxxx yyyy 0000
Prenez ce qu’il y a dans le registre sY, cela fait une adresse mémoire que l’on va lire et mettre dans le registre sX. L'intérêt est qu'après cette lecture on peut incrémenter sY et refaire un "FETCH" qui lira l'adresse suivante... ce qui est bien pratique dans une boucle.

### Jeu d'instructions

Le jeu d'instructions est présenté maintenant sans explications. Si quelqu’un veut ajouter une colonne aux tableaux ci-dessous pour expliquer ce que font les instructions ....

#### Les instructions de contrôle du picoblaze

    instructionopérande 1opérande 2Op-code JUMPaa aaaa aaaa11 0100 00aa aaaa aaaa JUMP Z,aa aaaa aaaa11 0101 00aa aaaa aaaa JUMP NZ,aa aaaa aaaa11 0101 01aa aaaa aaaa JUMP C,aa aaaa aaaa11 0101 10aa aaaa aaaa JUMP NC,aa aaaa aaaa11 0101 11aa aaaa aaaa CALLaa aaaa aaaa11 0000 00aa aaaa aaaa CALL Z,aa aaaa aaaa11 0001 00aa aaaa aaaa CALL NZ,aa aaaa aaaa11 0001 01aa aaaa aaaa CALL C,aa aaaa aaaa11 0001 10aa aaaa aaaa CALL NC,aa aaaa aaaa11 0001 11aa aaaa aaaa RETURN10 1010 0000 0000 0000 RETURN Z,10 1011 0000 0000 0000 RETURN NZ,10 1011 0100 0000 0000 RETURN C,10 1011 1000 0000 0000 RETURN NC,10 1011 1100 0000 0000

#### Les instructions de mouvement de données

    instructionopérande 1opérande 2Op-code LOADsXaaaa aaaa00 0000 xxxx aaaa aaaa LOADsXsY00 0001 xxxx yyyy 0000 FETCHsXaa aaaa00 0110 xxxx 00aa aaaa FETCHsX(sY)00 0111 xxxx yyyy 0000 STOREsXaa aaaa10 1110 xxxx 00aa aaaa STOREsX(sY)10 1111 xxxx yyyy 0000 INPUTsXaaaa aaaa00 0100 xxxx aaaa aaaa INPUTsX(sY)00 0101 xxxx yyyy 0000 OUTPUTsXaaaa aaaa10 0100 xxxx aaaa aaaa OUTPUTsX(sY)10 0101 xxxx yyyy 0000

#### Instructions arithmétiques

    instructionopérande 1opérande 2Op-code ADDsXaaaa aaaa01 0010 xxxx aaaa aaaa ADDsXsY01 0011 xxxx yyyy 0000 ADDCYsXaaaa aaaa01 1010 xxxx aaaa aaaa ADDCYsXsY01 1011 xxxx yyyy 0000 SUBsXaaaa aaaa01 1100 xxxx aaaa aaaa SUBsXsY01 1101 xxxx yyyy 0000 SUBCYsXaaaa aaaa01 1110 xxxx aaaa aaaa SUBCYsXsY01 1111 xxxx yyyy 0000 COMPAREsXaaaa aaaa01 0100 xxxx aaaa aaaa COMPAREsXsY01 0101 xxxx yyyy 0000

#### Instructions logiques

    instructionopérande 1opérande 2Op-code ANDsXaaaa aaaa00 1010 xxxx aaaa aaaa ANDsX(sY)00 1011 xxxx yyyy 0000 ORsXaaaa aaaa00 1100 xxxx aaaa aaaa ORsX(sY)00 1101 xxxx yyyy 0000 XORsXaaaa aaaa00 1110 xxxx aaaa aaaa XORsX(sY)00 1111 xxxx yyyy 0000 TESTsXaaaa aaaa01 0010 xxxx aaaa aaaa TESTsX(sY)01 0011 xxxx yyyy 0000

#### Instructions de décalage et rotations

    instructionopérande 1opérande 2Op-code SR0sX10 0000 xxxx 0000 1110 SR1sX10 0000 xxxx 0000 1111 SRXsX10 0000 xxxx 0000 1010 SRAsX10 0000 xxxx 0000 1000 RRsX10 0000 xxxx 0000 1100 SL0sX10 0000 xxxx 0000 0110 SL1sX10 0000 xxxx 0000 0111 SLXsX10 0000 xxxx 0000 0010 SLAsX10 0000 xxxx 0000 0000 RLsX10 0000 xxxx 0000 0100
Une figure nous expliquera un peu ces instructions :
et vous en déduirez facilement ce qui se passe pour les décalages gauches.

#### Instructions d'interruptions

    instructionopérande 1opérande 2Op-code ENABLEINTERRUPT11 1100 0000 0000 0001 DISABLEINTERRUPT11 1100 0000 0000 0000 RETURNIENABLE11 1000 0000 0000 0001 RETURNIDISABLE11 1000 0000 0000 0000

### Comparaison des mnémoniques et directives

Si vous développez autour du PicoBlaze vous serez amené à utiliser plusieurs assembleurs très proches mais pas complètement similaires, celui du KCPSM3 et celui du simulateur PBlazeIDE. À noter que si vous travaillez sous Linux, vous n'aurez pas ce problème. En effet l'unique environnement kpicosim permet de simuler d’une part et de générer la ROM d’autre part.
Nous présentons donc maintenant les différents mnémoniques des deux environnements Windows. KCPSM3 est l'assembleur fourni par Xilinx pour générer les ROMs à partir du programme tandis que PBlazeIDE est un environnement de simulation que l’on peut télécharger gratuitement.

#### Les différents mnémoniques du picoblaze

    KCPSM3PBlazeIDE addcyaddc subcysubc comparecomp store sX, (sY)store sX, sY fetch sX, (sY)fetch sX, sY input sX, (sY)in sX, sY input Sx,KKin sX,$KK ouput sX, (sY)out sX, sY returnret returnireti enable interrupteint disable interruptdint
Il existe d'autres différences avec les directives que nous présentons maintenant.

#### Les différents directives du picoblaze

    FonctionKCPSM3PBlazeIDE positionnement de codeaddress 3FForg $3FF constanteconstant MAX, 3FMAX equ $3F alias de registrenamereg addr, s2addr equ s2 alias de portsconstant in_port, 00in_port dsin $00 constant out_port 10out_port dsout $10 constant bi_port, 0Fbi_port dsio $0F

## Mon premier programme en assembleur

Nous avons déjà eu l’occasion de présenter quelques champs à la section 3-1. Nous allons présenter d'autres possibilités maintenant. Regardez le programme ci-dessous :
On distingue dans ce programme une étiquette , une définition symbolique, un commentaire et la définition de l'origine du programme. L'étiquette est un nom suivi de deux points, ici "suite:". La définition symbolique est m pour la valeur 10 (en hexadécimal). L'origine du programme est en adresse 300 (hexadécimal) ; on utilise la directive ADDRESS.

## Les structures de contrôle

On présente maintenant les structures de contrôle pour le PicoBlaze. Les structures de contrôle utilisent l'instruction COMPARE qu’il faut bien connaître. On la rencontre sous deux formes :
Cette instruction est mieux comprise avec son pseudocode qui montre comment les deux drapeaux Carry et Zero sont affectés par cette instruction :
Vous allez pouvoir comprendre les cas particuliers présentés maintenant.

### Structure si-alors-sinon

En écriture en langage C cette structure de contrôle test si sinon s'écrit :
En assembleur PicoBlaze cela s'écrit :
Pour les autres sauts conditionnels voir dans la liste des instructions.

### Structure répéter ... tant que ... et structure tant que...

On donne la structure correspondante dans plusieurs cas particuliers. Remarquez les difficultés pour le passage de s0>=5 à s0>5.
La comparaison suivante est basée sur le fait qu'une comparaison est équivalente à une soustraction et qu'ainsi le drapeau C est positionné si et seulement si le résultat de la soustraction est strictement négatif. Attention ce résultat général n’est pas intuitif.
La comparaison suivante est plus complexe à réaliser car aucune instruction de saut n'est conditionnée par les deux drapeaux Z et C simultanément. Un moyen d'y parvenir est d'inverser la comparaison ce qui nécessite de mettre 5 dans un registre !

### Structure for ...

En utilisant le plus possible les définitions symboliques on peut réaliser ce type de boucle avec un programme du type :

### L'adressage indexé

Indexé : l'adresse est calculée par le contenu du registre sX utilisé dans l'adressage. Ce registre est mis entre parenthèses dans ce cas. L'espace mémoire étant limité à 64 octets, seuls les 6 bits de poids faibles sont utilisés dans ce mode d'adressage.

### Les sous-programmes le switch et le PicoBlaze
#### Les sous-programmes

Les instructions CALL et RETURN sont destinées à l'appel de sous-programme et son retour. Expliquer le rôle de la pile pour les appels imbriqués. La pile ne comporte que 31 niveaux est n'est manipulable par aucune instruction spécifique. Ceci est inconvénient pour les langages évolués qui ne peuvent pas réaliser le passage de paramètres en utilisant la pile. Cela veut aussi dire qu’il n'y aura donc pas d'initialisation de la pile en début de programme.

#### La programmation de suivant le cas

La programmation du test selon s'écrit en C :
Si action_n est un sous programme il faut le remplacer par action_n en C. Les "break" imposent des conditions exclusives sur la variable. S'ils ne sont pas présents (car non obligatoires) l'algorithme exécuté peut se traduire : AU CAS OU
* condition 1, FAIRE : action_1 et toutes les suivantes
* condition 2, FAIRE : action_2 et toutes les suivantes
* condition 3, FAIRE : action_3 et toutes les suivantes
* condition 4, FAIRE : action_4 et la suivante
* DANS LES AUTRES CAS, FAIRE : action_0
Voici comment tout ceci se met en forme en assembleur PicoBlaze :
les actions "action_x" ne sont pas de vrais sous-programmes (appelés avec CALL finissant par RETURN).
Nous allons maintenant donner des exemples de morceau de code afin d'améliorer l'apprentissage de l'assembleur du picoBlaze.

### Code utile pour le PicoBlaze

Nous allons donner un peu de code pour les personnes intéressées par développer du code.

#### Triple boucle d'attente

Voici un code qui réalise un sous-programme d'attente d'environ une seconde :
Augmenter la valeur A0 augmentera la période.

#### incrémentation et décrémentation BCD

Lorsqu'on désire sortir le résultat d'un compteur sur des afficheurs sept segments il est intéressant d’avoir un compteur BCD. Voici un sous-programme qui réalise cela :
Voici le petit frère qui décrémente :
Une fois cela calculé, il faut l'afficher et cela commence par un transcodage.

#### Transcodeur

Il n’est pas possible de mettre dans ce morceau de code tous les transcodages possibles et imaginables.
Le code donné dans cette section nécessite les déclarations :
Tout commence par une initialisation. Ici on donne du code pour un afficheur sept segment en hexadécimal avec un '1' pour éteindre le segment.
La première ligne d'initialisation qui est sensée afficher '0' nous montre que le segment 'g' est poids faible.
Une boucle infinie peut utiliser cela :
Maintenant au tour des poids forts et la fin de la boucle :
Une routine d'attente même trop rapide peut fonctionner :

## La partie matérielle du PicoBlaze
### Embarquer le PicoBlaze

Le PicoBlaze est un microcontrôleur destiné à être embarqué dans un FPGA. Ce qui caractérise ce type de composant est qu’il comporte en général peu de logique d'interfaçage vers l'extérieur (PORTS) et encore moins de logique interne traditionnelle (timer, comparaison, capture, ...). La raison à cela est qu’il va se trouver dans un FPGA où le développement de cette logique externe se fait très facilement. On peut donc l'adapter à notre propre problème. Cette absence de ports va rendre l'architecture de ce micro-contrôleur facile à comprendre. Commençons par présenter l'aspect fonctionnel du PicoBalze :
L'article Wikipédia sur le PicoBlaze vous donne l'entité équivalente à cette figure en VHDL.
Télécharger le PicoBlaze consiste à obtenir le code source correspondant à la figure ci-dessus. Vous disposez de l'entité et de l'architecture correspondante avec son code source. Mais ce code n'est lisible que par un spécialiste des composants FPGA de chez Xilinx.
La plus petite application consiste à lui ajouter une mémoire morte avec les instructions correspondantes dedans. Cela nous donne le schéma ci-dessous où toutes les entrées ainsi que les sorties seront reliées à de la logique du FPGA comme cela sera partiellement détaillé dans la section suivante.
Remarquez dans cette figure que la ROM est synchrone et qu'elle utilise la même horloge que le PicoBlaze.

### Les entrées sorties

Nous allons apprendre dans ce chapitre à interfacer des ports au PicoBlaze. Commençons par les entrées.

#### Interfacer un PORT d'entrée

Le diagramme temporel correspondant est présenté maintenant.
Pour faire l'acquisition d'une valeur externe, il faut réaliser une instruction « INPUT » en précisant un numéro de port sur 8 bits. Il y a donc 256 ports possibles.
* Le schéma donné ci-dessous est incorrect et sera corrigé plus tard. Il ne faut pas utiliser le signal "read_strobe" pour une lecture de PORT sauf dans le cas où l’on a un FIFO en entrée si l’on en croit la documentation Xilinx !!! La section suivante, par contre, est correcte : utilisation de "write_strobe" pour une écriture dans un PORT. Cela nous montre la dissymétrie entre une entrée et une sortie.
On remarque que l'instruction « INPUT » est réalisée en deux fronts d'horloge (comme toutes les instructions) et qu'un signal READ_STROBE est disponible sur un seul front d'horloge. La partie matérielle peut être réalisée comme suit :
Dans cet exemple on dispose de 4 registres 8 bits pour lesquels on n'a pas dessiné les entrées arrivant de l'extérieur. Le multiplexeur ainsi que la logique combinatoire de décodage peuvent n'utiliser que les 2 bits de poids faible du signal PORT_ID. Il est possible de rajouter un registre entre le multiplexeur et l'entrée du PicoBlaze pour augmenter les performances (pipeline).
On donne un exemple de programme VHDL correspondant aux deux composants ajoutés, le multiplexeur et la logique de décodage ainsi qu'une partie du registre.

#### Interfacer un PORT de sortie

Le diagramme temporel de l'instruction OUTPUT est donnée ci-dessous.
On n'a pas représenté les signaux de sorties du circuit de décodage mais ils sont légèrement décalés par rapport à WRITE_STROBE puisqu’ils l'utilisent. Nous montrons ci-dessous un exemple de circuit pouvant réaliser une sortie sur un des quatre ports présents :
Pour quatre ports seuls les deux bits de poids faible de PORT_ID[7:0] peuvent être utilisés. On donne pour une meilleure compréhension les programmes VHDL correspondants :
Remarquons qu’il est possible de simplifier largement le décodage si on utilise comme numéros de PORT : 1, 2, 4 et 8 (au lieu de 1, 2, 3 et 4) et du coup les 4 bits de poids faible de PORT_ID[7:0] :
Un autre problème va nous intéresser maintenant : les interruptions.

### Les interruptions

La gestion des interruptions est en général très dépendante de l’application à réaliser. Encore une fois le PicoBlaze ne comprend par défaut que le minimum de logique pour gérer les interruptions. Quand on dit minimum, c’est le minimum : même pas de gestion de timer par exemple. C'est à l'utilisateur de réaliser la logique correspondante chargée de réaliser le seul signal « INTERRUPT » présent (une seule source d'interruption donc par défaut). L'activation de ce signal force le PicoBlaze à réaliser un saut vers l'adresse 3FF (dernière adresse de la mémoire programme). À noter qu'après un RESET le signal d'interruption est inactif : une instruction « ENABLE INTERRUPT » doit être réalisée pour l'activer. À noter que l'exécution d'une interruption préserve les drapeaux C et Z du registre d'état.

#### Partie logicielle

L'architecture d'un programme assembleur gérant une interruption sera comme :
Lorsqu'une interruption (1) arrive, le programme est dérouté (2) à l'adresse 3FF. Cette adresse branche (3) à la routine d'interruption proprement dite. Puis quand la routine est terminée on revient là où l’on a été interrompu (4).

#### Chronogramme

La routine d'interruption n’est pas instantanée dès réception du signal d'interruption. L'iinstruction en cours continue à s'exécuter puis une fois terminée l’ensemble des opérations décrites dans la section précédente sont synchronisées avec l'horloge comme indiqué sur le chronogramme suivant :

#### Partie matérielle

Un exemple simple comprenant une seule source d'interruption est présenté maintenant.
Les signaux SET et RESET sont asynchrones.
* Le schéma ci-dessus trouvé dans la documentation Xilinx ne fonctionne pas correctement si la durée du signal d'interruption est trop courte (et si la bascule D comporte une horloge). Nous remplacerons ce schéma à l’occasion en faisant disparaître ce bandeau.
L'entrée "interrupt" du picoBlaze est active sur niveau haut. Les interruptions doivent être autorisées à l'intérieur du programme avec une instruction "ENABLE INTERRUPT" pour que cette entrée soit prise en compte. Il est bon de se souvenir aussi que quand le picoBlaze commence l'exécution d'une interruption, il désactive automatiquement les interruptions. Cela restera ainsi sauf si votre retour d'interruption est réalisé avec un "RETURNI ENABLE" comme instruction.
Voici un morceau de code VHDL donné par Ken Chapman dans le forum picoBlaze pour gérer les interruptions :
Ce code ne fonctionne correctement que si le signal "extint" est synchrone.