# Very High Speed Integrated Circuit Hardware Description Language/Système monopuce compatible avec les PIC 16C57

Un des microcontroleur 8 bits les plus utilisés a été fabriqué par Microchip : c’est le 16F84. Un projet d'informatique embarquée le mettant en œuvre est décrit dans le chapitre sur CQPIC 16F84. Pour le moment nous allons nous intéresser à encore plus petit, le 16C57. Il possède un jeu d'instructions plus réduit : ses instructions sont codées sur 12 bits contre 14 sur le 16F84. Nous nous proposons dans ce chapitre de réaliser un système monopuce à l'aide d'un programme VHDL simulant un PIC 16C57 et si possible de le programmer en C. Je parle bien de simulation car un programme VHDL décrivant un processeur n'est jamais complètement équivalent au processeur que vous pouvez acheter.
Le contenu de ce paragraphe peut sembler redondant avec les chapitres CQPIC 16F84 et ATMega8. Je rappelle que tous ces chapitres sont des projets qui font exactement la même chose, gérer un jeu de pong, d'où cette redondance. Il vous est demandé de choisir votre architecture et de lire le chapitre correspondant. Désolé, mais il en faut pour tous les goûts !
Nous allons commencer par rappeler les instructions assembleurs du PIC 16C57. A priori on ne programmera pas en assembleur, mais utiliser des architectures si petites demande parfois de savoir le faire.

## Présentation du PIC 16C57

Dans cette section nous allons présenter le PIC 16C57 existant, je veux dire le composant commercialisé. Nous nous intéresserons au processeur softcore un peu plus loin.

### Mémoire RAM du 16C57

Nous allons commencer par présenter le plus difficile, l'architecture du banc de registres (Register File dans la terminologie anglo-saxonne que l’on pourrait traduire par dossier/classeur de registres, mais je préfère registres mémoire ou mémoire de registres ou banc de registres). Je parle de difficultés parce qu’il y a plusieurs banques mémoires ce qui est une caractéristique des architectures 8 bits Microchip.
Le banc de registres est divisée en quatre banques. Nous avons d’abord la série des (vrais) registres jusqu'au PORTC. Cette partie de mémoire est suivie par 8 registres d’usage général pouvant être utilisés comme mémoire simple. Cet ensemble est identique quelle que soit la banque choisie. Ensuite vient une série de registre généraux qui eux dépendent de la banque mémoire choisie. Voici le schéma simplifié de cette architecture.

#### RAM et Registres

    Adr.Banque0Banque1Banque2Banque3Adr. 00hIndirect addr.Indirect addr.Indirect addr.Indirect addr.60h 01hTMR0TMR0TMR0TMR0 02hPCLPCLPCLPCL 03hSTATUSSTATUSSTATUSSTATUS 04hFSRFSRFSRFSR 05hPORTAPORTAPORTAPORTA 06hPORTBPORTBPORTBPORTB 07hPORTCPORTCPORTCPORTC 08h - 0Fh8 registres généraux identiques sur les 4 banques28h - 2FH48h -4Fh68h -6FH 10h - 1Fh16 registres généraux16 registres généraux16 registres généraux16 registres généraux70H - 7FH
Calculons la taille de la mémoire de registres généraux :
4 x 16 + 1 x 8 = 72 octets.
C'est tout ce qu’il y a de disponible comme mémoire dans cette architecture.
Dans la suite de ce cours, des registres TRISx et OPTION pourront apparaître. Ils ne sont pas présents dans la description que l’on vient de faire car ils ne sont accessibles que par des instructions spécialisées TRIS et OPTION présentées dans la section suivante.

### Le jeu d'instructions du 16C57

Cette section est difficile à comprendre. Même si elle ne fait intervenir que des notions du niveau indiqué, il est conseillé d'avoir du recul sur les notions présentées pour bien assimiler ce qui suit. Cependant, ce contenu n'est pas fondamental et peut être sauté en première lecture.
Voici l’ensemble des instructions 12 bits du PIC 16C57. Vous trouverez en colonne de gauche le code binaire de l'instruction se trouvant au centre puis une courte explication de ce que fait l'instruction.
Les opérandes peuvent être de plusieurs types:
* f : adresse mémoire de registres (register file address) de 00 à 7F
* W : registre de travail
* d : sélection de destination : d=0 vers W, d=1 vers f
* pp : numéro de PORT entre 1 et 3 sur deux bits
* bbb : adresse de bit dans un registre 8 bits (sur 3 bits)
* k : champ littéral (8, ou 9 bits)
* PC compteur programme
Jeu d'instructions 12-bit pour PIC 16C57 :
| Opcode (binary) | Mnemonic | Description |
| 0000 0000 0000 | NOP | Pas d'opération |
| 0000 0000 0010 | OPTION | Chargement du registre OPTION avec le contenu de W |
| 0000 0000 0011 | SLEEP | arrête le processeur |
| 0000 0000 0100 | CLRWDT | Reset du timer watchdog |
| 0000 0000 01pp | TRIS P | Chargement de W dans le registre de direction (P=1..3) |
| 0000 001 fffff | MOVWF f | Recopie de W dans f |
| 0000 010 xxxxx | CLRW | Positionne W à 0 (idem à CLR x, W) |
| 0000 011 fffff | CLRF f | Positionne f à 0 (idem à CLR f, F) |
| 0000 10d fffff | SUBWF f, d | Soustrait W de (d = f − W) |
| 0000 11d fffff | DECF f, d | Décrément f (d = f − 1) |
| 0001 00d fffff | IORWF f, d | OU Inclusif W avec F (d = f OR W) |
| 0001 01d fffff | ANDWF f, d | ET entre W et F (d = f AND W) |
| 0001 10d fffff | XORWF f, d | OU Exclusif W avec F (d = f XOR W) |
| 0001 11d fffff | ADDWF f, d | Additionne W avec F (d = f + W) |
| 0010 00d fffff | MOVF f, d | recopie F (d = f) |
| 0010 01d fffff | COMF f, d | Complement f (d = NOT f) |
| 0010 10d fffff | INCF f, d | Incrément f (d = f + 1) |
| 0010 11d fffff | DECFSZ f, d | Decrément f (d = f − 1) et saut si zero |
| 0011 00d fffff | RRF f, d | Rotation droite F (rotation droite avec la retenue) |
| 0011 01d fffff | RLF f, d | Rotation gauche F (rotation gauche avec la retenue) |
| 0011 10d fffff | SWAPF f, d | f>>4) |
| 0011 11d fffff | INCFSZ f, d | Incrément f (d = f + 1) et saut si zero |
| 0100 bbb fffff | BCF f, b | RaZ d'un bit de f (Clear bit b of f) |
| 0101 bbb fffff | BSF f, b | Mise à 1 d'un bit de f (Set bit b of f) |
| 0110 bbb fffff | BTFSC f, b | test de bit de f, saute si zéro (Test bit b of f) |
| 0111 bbb fffff | BTFSS f, b | test de bit de f, saute si un (Test bit b of f) |
| 1000 kkkkkkkk | RETLW k | Positionne W à k et retour |
| 1001 kkkkkkkk | CALL k | Sauve l'adresse de retour, charge PC avec k |
| 1010 kkkkkkkkk | GOTO k | saut à l'adresse k (9 bits!) |
| 1100 kkkkkkkk | MOVLW k | Chargement littéral de W (W = k) |
| 1101 kkkkkkkk | IORLW k | OU Inclusif littéral avec W (W = k OR W) |
| 1110 kkkkkkkk | ANDLW k | ET littéral avec W (W = k AND W) |
| 1111 kkkkkkkk | XORLW k | OU exclusif or littéral avec W (W = k XOR W) |
Dans la terminologie Microchip, littéral signifie en adressage immédiat.
Dans les instructions faisant intervenir la mémoire RAM (comme MOVWF f), f désigne une adresse mémoire qui semble être codée par fffff (5 bits) dans l'op-code. En fait il faut 7 bits pour coder l'adresse puisqu'elle peut aller jusqu'à 7Fh. Les deux bits manquants viennent d'ailleurs. D'où viennent-ils ? Les trois bits de poids fort du registre STATUS sont réservés à cet usage. Mais comme on l'a expliqué, la pagination est sur quatre pages différentes ce qui nécessite seulement deux bits (b6 et b5).

### Mémoire ROM du 16C57

La taille de la ROM est de 2048 x 12 bits. C'est une mémoire très modeste qui limitera par la suite la taille des programmes que l’on fera tourner dans cette architecture, surtout si les programmes proviennent d'un compilateur.

### Les trois ports du 16C57

Le PIC(R) 16C57 possède trois PORTs bidirectionnels appelés PORTA, PORTB et PORTC. La direction est choisie avec un registre TRIS dans lequel on écrit avec une instruction spéciale.
Avant de poursuivre, nous allons essayer de faire un parallèle avec le PIC 16C57 que l’on vient de décrire et le processeur softcore correspondant.

## Le processeur softcore SLC1657

Dans la suite nous parlerons indifféremment de processeur softcore ou de cœur de processeur et le plus souvent encore de cœur.
Le SLC1657 est un cœur de processeur compatible avec le PIC® 16C57 de Microchip. On peut le trouver sur Internet : slc1657.zip
Il a été développé par SiliCore (sa dernière version date de septembre 2003) : Présentation du projet
Voici maintenant une description du cœur sous forme de programme VHDL.
Pour ce cœur, EPRC correspond au bus d'adresse de la ROM, tandis que "ROM" correspond au bus de données de cette même ROM. La RAM est gérée quant à elle avec EALU comme bus d'adresses et avec GP comme bus de données.
À noter que contrairement au PIC® 16F57, le composant SLC1657 utilise une entrée spécifique TMRCLK pour le timer0 (pour le PIC® c’est le bit b4 (appelé T0CKl) du PORTA).
L'ensemble des fichiers disponibles dans le projet SLC1657 ne décrit pas la RAM, la ROM et pas complètement les PORTs. Les concepteurs l'ont décidé car ces composants sont trop spécifiques aux fabricants.
Avant de passer aux périphériques nécessaires au bon fonctionnement, il nous faut aborder le problème très spécifique des ports.

### Les trois ports du SLC1657

Les ports sont des entités très spécifiques dans un micro contrôleur car ils sont bidirectionnels. Ce côté bidirectionnel est difficile à implanter de manière générale dans un FPGA puisqu’il dépend du fabricant et donc de l'environnement d’utilisation. Alors, puisqu'elle dépend de l'environnement de programmation, cette description VHDL est laissée à l'utilisateur du cœur SLC1657.
En principe, cette bidirectionnalité est gérée par un registre spécial TRIS associé à chacun des ports. Ces registres spécifiques existent dans le cœur, ils s'appellent PCOUT0, PCOUT1 et PCOUT2. Ils sont tous sur 8 bits, contrairement au PIC® 16F57 pour lequel le PORTA ne possède que 4 bits et peuvent être utilisés soit comme ports généraux en sortie, soit pour la gestion bidirectionnelle. Les ports proprement dit se décomposent alors en port d'entrée PTIN0, PTIN1 et PTIN2 et en ports de sortie PTOUT0, PTOUT1 et PTOUT2.
Remarque : pour des raisons de compatibilité le port PCOUTi n’est pas accessible de manière normale. Si i=0 l'écriture dans PCOUT0 se fait par l'instruction :
tandis qu'en assembleur on écrira :
même si les commentaires de ce programme ne sont pas appropriés dans le cas où l’on n'a pas un port bidirectionnel (ce qui, je le rappelle, est notre cas).
Abordons maintenant les deux autres périphériques nécessaires, la RAM et la ROM. Ces deux composants ne font pas partie du cœur tout simplement parce que les implantations de ceux-ci sont spécifiques aux FPGA cibles.

### La RAM

Nous avons déjà eu l’occasion de présenter celle-ci, en tout cas du point de vue de sa capacité. Sa spécificité est qu’il s'agit d'une mémoire à écriture synchrone mais à lecture asynchrone. Il est facile de trouver des exemples d'une telle mémoire en VHDL. Nous avons choisi :
Une mauvaise implémentation de cette mémoire est fatale au bon fonctionnement du processeur, surtout s'il est utilisé avec un compilateur C qui utilise des variables locales.

### La ROM

C'est là que se situe le programme à exécuter. Puisque tout compilateur ou assembleur génère un fichier hex, il nous est nécessaire de transformer ce fichier hex en fichier VHDL. Ceci est laissé à un programme C décrit un peu plus loin. Commençons à présenter un exemple de ROM.
Cet exemple montre un contenu mais chaque programme donnera une ROM différente. Il est à noter que ce que l’on présente est du VHDL alors que chaque compilateur ou assembleur ne délivre qu'un fichier au format HEX. Il faudra donc un utilitaire pour transformer le fichier HEX en fichier VHDL car il s'agit d'une opération qui peut se faire automatiquement. Nous utiliserons un programme en C que nous donnons en annexe 1.

### Assemblage du cœur, des mémoires et du module VGA

À la fin de ce précédent chapitre Interfaces VGA, nous avons présenté un module capable de dessiner une balle et deux raquettes, ces trois objets étant mobiles.
Utile aussi de lire ou de relire l'article sur le VGA.
Donnons une image simplifiée de ce que l’on cherche à faire.
La gestion de l'écran VGA peut se présenter comme une boite, certes complexe, mais qui sait dessiner :
* une balle
* deux raquettes
La seule chose qui sera demandé au processeur sera de gérer toutes les coordonnées de ces éléments.
Toute interface entre un processeur et de la logique externe passe par un ou plusieurs ports. Nous donnons maintenant l'assemblage complet de notre projets sous la forme de choix technologiques avec d'un côté les entrées sorties (E/S) du module VGA et de l'autre les entrées sorties du processeur softcore.

#### choix technologiques

    E/S module VGAE/S SLC1657 x_rect<9:8>TRISA<1:0> x_rect<7:0>PORTA<7:0> y_rect<9:8>TRISB<1:0> y_rect<7:0>PORTB<7:0> y_raqG<7:0>PORTC<7:0> y_raqD<7:0>TRISC<7:0>
Ces choix montrent qu'on utilise 4 PORTs 8 bits pour faire les deux signaux 10 bits nécessaires aux deux coordonnées du rectangle (qui sera une balle) et deux ports 8 bits pour faire les deux entrées des positions des raquettes droites et gauches.

#### Exercice 1

Donner le programme VHDL correspondant aux choix technologiques ci-dessus qui assemble le composant SLC1657, la RAM, la ROM et le module VGA. Voir VGA-Exercice 2 pour le composant VGAtop, description de la RAM, de la ROM et le composant SLC1657.

#### Modifier les coordonnées de la balle et des raquettes en C

On va s'intéresser ici à la gestion de la coordonnée X de la balle. Le sous-programme responsable de cette action s’appelle "setX".

##### Le sous-programme "setX"

Notre problème est tout simple : si l’on regarde le tableau des choix technologiques, on s'aperçoit qu'on veut prendre une valeur sur 10 bits (en fait passée en paramètre comme "unsigned int" (soit 16 bits)), de la couper en deux pour la ranger dans deux registres différents. Le sous programme en C pur est alors :
Le contenu de ce programme est complètement déterminé par la partie matérielle.

#### Utiliser le Timer0 avec le langage C

Nous aimerions bien explorer maintenant la possibilité de faire une attente (malheureusement active) en utilisant le seul et unique timer de notre architecture, à savoir le timer0. Commençons par présenter la documentation du Timer0 sous la forme d'un schéma :
La figure ci-dessus montre clairement la gestion du timer0 du SLC1657. Elle est légèrement différente de ce qui est matériellement réalisé par Microchip dans son PIC 16C57. Le registre général de gestion du timer 0 s’appelle OPTION dans le PIC et TCO dans le SLC1657. Dans le programme en C on utilisera naturellement "OPTION" pour désigner ce registre.
On désire réaliser une attente la plus longue possible avec le timer 0. Une division par 256 sur le quartz interne est donc nécessaire. La figure vous permet de trouver les valeurs à mettre dans le registre OPTION.
Voici donc un exemple de code :
La boucle d'attente est réalisée par un "while".
Le sous-programme "wait" de l'exemple ci-dessus appelé avec une valeur de 250 est un peu plus rapide qu'un appel de
avec une valeur de 30000.
Le PIC 16C57 ne possède pas d'interruptions. Il est donc impossible de gérer la temporisation avec des interruptions. Les attentes seront donc actives, c'est-à-dire gérées par des boucles d'attentes : impossible de faire une autre tâche pendant l'attente.

#### Programme complet de gestion simple de la balle

Nous présentons maintenant un programme simple de gestion de la balle avec des rebonds :
Ce programme doit être compilé dans l'environnement MPLAB de Microchip. Cela nous donnera un fichier d'extension ".hex" qu’il faudra transformer en VHDL. Un programme en C capable de faire cette transformation est donné dans l'annexe qui suit.

## Annexe 1 : transformer un fichier hex en fichier VHDL

Nous donnons sans plus d'explications un programme source en C permettant de transformer un fichier d'extension ".hex" provenant d'un compilateur ou d'un assembleur en fichier ".VHD" pouvant être directement inclus dans le projet VHDL.
Le fichier VHDL généré par ce programme comporte parfois une erreur sur le premier when : deux fois un When to_integer(unsigned(Addr)) = 0000 . C'est d'ailleurs le cas du seul exemple donné dans ce document. Je n'ai pas vérifié la gravité de ce problème mais il me semble que je n'ai jamais rencontré de disfonctionnement de ROM dans ce projet... ce qui me laisse penser que ce n’est pas très grave... mais il serait plus sage de vérifier tout cela.

## Conclusion

On est très vite confronté au problème de la petite taille mémoire RAM du cœur. L'exemple typique a été les comparaisons pour déterminer si la balle heurtait la/les raquettes. Dès que cette comparaison était faite en C avec le sous-programme wait sans timer, on n'avait pas assez de mémoire RAM. Ce calcul a failli être déporté dans la partie matérielle décrite en VHDL avant que l’on s'aperçoive que l’utilisation d'un timer résolvait le problème.
Nous n'avons personnellement pas eu le temps de comprendre exactement la façon dont étaient gérées les variables avec le compilateur HitechC que l’on a utilisé. Des économies de place RAM dans le code généré n'ont ainsi pas pu être obtenues.
Lors de la réalisation de ce projet, nous étions obsédé par la possibilité de faire "tourner" du C dans un FPGA. C'est la première fois que nous en avions l’occasion en effet. Il nous semble après coup, que les limitations RAM de cette architecture, conduisent plutôt à l’utiliser en assembleur qu'en langage C.
Depuis la rédaction de cette conclusion, nous avons appris comment augmenter la taille de la RAM et de la ROM du point de vue du compilateur C dans l'environnement MPLAB (voir Embarquer un PIC16F84). Nous pensons qu’il n’est pas très difficile d'augmenter la taille des bus correspondant dans ce cœur, mais nous ne l'avons pas testé.
Comparaison SiliCore1657 et Picoblaze® :
| | SiliCore1657 | Picoblaze® |
| Statut | Libre | IP Core Xilinx |
| Code source | Disponible au téléchargement | Disponible au téléchargement chez Xilinx mais illisible |
| Taille dans FPGA | 30% d'un spartan 3 - 200k | 4% d'un spartan 3 - 200k |
| RAM | 72 octets | 64 octets |
| Registres | 7 spécialisés | 16 d’utilisation générale |
| PORTS | 3 mais peut être étendu | peut être étendu jusqu'à 256 |
| ROM | 2048 x 12 bits | 1024 x 18 bits |
| Pile | 2 niveaux | 31 niveaux |
| Environnement de développement | MPLAB gratuit et connu qui trourne aussi sous Linux-wine | PBlazeIDE (mediatronix) et KPicosim sous Linux |
| Langages | C, assembleur | assembleur et pbcc un compilateur C pour picoBlaze dérivé de sdcc |
| Interruptions | Aucune | Dispo mais à gérer soi-même. Existe un gestionnaire chez opencores.org |
| Documentation | Très bien documenté en anglais | Très bien documenté en anglais |
Ces deux architectures ont à peu près les mêmes champs d'application : petits programmes et grosses parties matérielles autour. Nous avons une petite préférence pour le Silicore1657 parce qu’il existe un compilateur C qui le cible. Cette remarque n'est plus d'actualité puisque c’est vrai aussi pour le picoBlaze.

## Remarque finale

Nous allons classer ce chapitre comme terminé alors qu’il y aurait bien des choses à encore explorer. La raison est simple : nous n'avons plus l'intention d’utiliser cette architecture car elle est trop limitée pour l’utilisation d'un compilateur C.
Les 30% pour la taille dans le spartan3 surestiment probablement la taille réelle de ce projet. Depuis la rédaction de ce chapitre nous avons appris à gérer les mémoires programmes des soft-processeurs. Un chapitre a d'ailleurs été ajouté pour cela (entre autres) : Programmer in Situ et déboguer. Mais nous n'avons pas l'intention de revenir sur ce projet et améliorer cette utilisation des mémoires. Le PIC16F84 utilisé dans le chapitre suivant sera notre base de travail pour les PICs dorénavant.