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

Une page de Wikiversité.
Aller à : Navigation, rechercher
Début de la boite de navigation du chapitre
Système monopuce compatible avec les PIC 16C57
Icône de la faculté
Chapitre no7
Leçon : Very High Speed Integrated Circuit Hardware Description Language
Chap. préc. : Micro contrôleur embarqué : le PicoBlaze
Chap. suiv. : Embarquer_un_PIC_16F84
fin de la boite de navigation du chapitre


Un des microcontroleur 8 bits les plus utilisé 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.

Remarque

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.

Sommaire

[modifier] 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.

[modifier] 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. Banque0 Banque1 Banque2 Banque3 Adr.
00h Indirect addr. Indirect addr. Indirect addr. Indirect addr. 60h
01h TMR0 TMR0 TMR0 TMR0
02h PCL PCL PCL PCL
03h STATUS STATUS STATUS STATUS
04h FSR FSR FSR FSR
05h PORTA PORTA PORTA PORTA
06h PORTB PORTB PORTB PORTB
07h PORTC PORTC PORTC PORTC
08h - 0Fh 8 registres généraux identiques sur les 4 banques 28h - 2FH 48h -4Fh 68h -6FH
10h - 1Fh 16 registres généraux 16 registres généraux 16 registres généraux 16 registres généraux 70H - 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.

Début d'une propriété

Les registres oubliés

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.

Début de la propriété


[modifier] Le jeu d'instructions du 16C57

Zeichen 114.svg

Cette section est difficile. 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 comprendre 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.


Début d'une propriété

Accès à la mémoire de registres

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).

Début de la propriété


[modifier] 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.

[modifier] 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.

[modifier] 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.

entity TOPLOGIC is
    port (
            EADR:       out std_logic_vector(  6 downto 0 );
            EALU:       out std_logic_vector(  7 downto 0 );
            EMCLK_16:   out std_logic;
            EPRC:       out std_logic_vector( 10 downto 0 );
            EWERAM:     out std_logic;
            GP:         in  std_logic_vector(  7 downto 0 );
            MCLK:       in  std_logic;
            PCOUT0:     out std_logic_vector(  7 downto 0 );
            PCOUT1:     out std_logic_vector(  7 downto 0 );
            PCOUT2:     out std_logic_vector(  7 downto 0 );
            PTIN0:      in  std_logic_vector(  7 downto 0 );
            PTIN1:      in  std_logic_vector(  7 downto 0 );
            PTIN2:      in  std_logic_vector(  7 downto 0 );
            PTOUT0:     out std_logic_vector(  7 downto 0 );
            PTOUT1:     out std_logic_vector(  7 downto 0 );
            PTOUT2:     out std_logic_vector(  7 downto 0 );
            PTSTB0:     out std_logic;
            PTSTB1:     out std_logic;
            PTSTB2:     out std_logic;
            PRESET:     in  std_logic;
            RESET:      in  std_logic;
            ROM:        in  std_logic_vector( 11 downto 0 );
            SLEEP:      out std_logic;
            TESTIN:     in  std_logic;
            TMRCLK:     in  std_logic
        );
end entity TOPLOGIC;

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).


Début d'une propriété

Ensemble implanté dans le processeur softcore

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.

Début de la propriété


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.

[modifier] 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 :

              // en langage C
              TRISA=0xFF;

tandis qu'en assembleur on écrira :

             ; assembleur
             movlw 255        ;1:input
             TRIS      PORTA ;PORTA en entree

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.

[modifier] 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 :

             library ieee;
             use ieee.std_logic_1164.all;
             use ieee.numeric_std.all;
             entity xilinx_one_port_ram_sync is
               generic(
                  ADDR_WIDTH: integer:=7;
                  DATA_WIDTH: integer:=8
               );
               port(
                  clk: in std_logic;
                  we: in std_logic;
                  addr: in std_logic_vector(ADDR_WIDTH-1 downto 0);
                  din: in std_logic_vector(DATA_WIDTH-1 downto 0);
                  dout: out std_logic_vector(DATA_WIDTH-1 downto 0)
                );
             end xilinx_one_port_ram_sync;
             architecture beh_arch of xilinx_one_port_ram_sync is
               type ram_type is array (2**ADDR_WIDTH-1 downto 0)
                    of std_logic_vector (DATA_WIDTH-1 downto 0);
               signal ram: ram_type;
             begin
               process (clk)
               begin
                  if (clk'event and clk = '1') then
                     if (we='1') then
                        ram(to_integer(unsigned(addr))) <= din;
                        end if;
                  end if;
               end process;
                         dout <= ram(to_integer(unsigned(addr)));
             end beh_arch;

Remarque

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.


[modifier] 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.

             library IEEE;
             use IEEE.std_logic_1164.all;
             use IEEE.numeric_std.all;
             entity PIC_ROM is
               port (
                          Addr : in std_logic_vector(10 downto 0);
                          Data : out std_logic_vector(11 downto 0));
               end PIC_ROM;
               architecture first of PIC_ROM is
               begin
                Data <=
                    "000000000000" When to_integer(unsigned(Addr)) = 0000 Else
                    "110011111111" When to_integer(unsigned(Addr)) = 0000 Else
                    "000000000101" When to_integer(unsigned(Addr)) = 0001 Else
                    "110000000000" When to_integer(unsigned(Addr)) = 0002 Else
                    "000000000110" When to_integer(unsigned(Addr)) = 0003 Else
                    "001000000101" When to_integer(unsigned(Addr)) = 0004 Else
                    "000000100110" When to_integer(unsigned(Addr)) = 0005 Else
                    "101000000100" When to_integer(unsigned(Addr)) = 0006 Else
                    "101000000000" When to_integer(unsigned(Addr)) = 2047 Else
                    "000000000000";
               end first;

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.

[modifier] Assemblage du cœur, des mémoires et du module VGA

Searchtool.svg Avant d'aller plus loin, relisez Interfaces VGA et PS/2.

À 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.

Début d'une propriété

Interface VGA et processeur

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.

Début de la propriété

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 VGA E/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.

[modifier] 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] 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".

[modifier] 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 :

void setX(unsigned int x){
  PORTA=x;
  TRISA=x>>8;
}

Le contenu de ce programme est complètement déterminé par la partie matérielle.

[modifier] 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 :

Le timer 0 dans le SLC1657

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.


Début de l'exemple

Exemple

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 :

void wait(unsigned char tempo){ // temporisation avec timer0
  OPTION=0x07; // div par 256 et source=quartz
  TMR0 =0;
  while(TMR0<tempo);
}

La boucle d'attente est réalisée par un "while".

Fin de l'exemple

Le sous-programme "wait" de l'exemple ci-dessus appelé avec une valeur de 250 est un peu plus rapide qu'un appel de

void wait(int tempo){ // temporisation classique
  int i; 
  for(i=tempo;i>0;i--); 
}

avec une valeur de 30000.


Remarque

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.


[modifier] Programme complet de gestion simple de la balle

Nous présentons maintenant un programme simple de gestion de la balle avec des rebonds :

#include <pic16F5x.h> // Programme pour Hitech C dans MPLAB
void setX(unsigned int x); 
void setY(unsigned int y); 
void wait(unsigned char tempo); 
unsigned int posRaqu_16;  
void main(){ 
int posX,posY; 
unsigned char raqD_y=0,raqG_y=0; 
signed char deltaX=1,deltaY=1; 
        while(1){        
                posX=113;                
                posY=101; 
                setX(posX); 
                setY(posY); 
                while(RC2==0); // attente départ 
                while( (posX>30) && (posX<580)){ 
                        posRaqu_16=raqD_y<<1;   
                        if ((posX>=574) && (posY<posRaqu_16+58) &&
                                                 (posY+10>posRaqu_16) && (deltaX>0)) deltaX= -deltaX; 
                        posRaqu_16=raqG_y<<1; 
                        if ((posX<=32) && (posY<posRaqu_16+58) &&
                                                (posY+10>posRaqu_16) && (deltaX<0)) deltaX= -deltaX; 
                        posX=posX+deltaX; 
                        setX(posX); 
                        if ((posY>=460) && (deltaY>0)) deltaY= -deltaY; 
                        if ((posY<=10) && (deltaY<0)) deltaY= -deltaY; 
                        posY=posY+deltaY; 
                        setY(posY); 
// gestion des raquettes 2bits PORTC/raquette 
                        if (RC0) if (raqG_y<215) raqG_y++; 
                        if (RC1) if (raqG_y>0) raqG_y--; 
// positionnement raquette gauche
                        PORTC=raqG_y; 
                        if (RC6) if (raqD_y<215) raqD_y++; 
                        if (RC7) if (raqD_y>0) raqD_y--; 
// positionnement raquette droite
                        TRISC=raqD_y; 
// atttente
                        wait(250); 
                        wait(250); 
                } 
        } 
} 
 
 
void setX(unsigned int x){ 
  PORTA=x; 
  TRISA=x>>8; 
} 
 
void setY(unsigned int y){ 
  PORTB=y; 
  TRISB=y>>8; 
} 
 
void wait(unsigned char tempo){ 
OPTION=0x07; // div 256 et source=quartz 
TMR0 =0; 
while(TMR0<tempo); 
}

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.

[modifier] 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.

//from Thomas A. Coonan (tcoonan@mindspring.com) and adapted for Xilinx by S. Moutou
#include <stdio.h> 
#include <stdlib.h> 
#include <string.h> 
 
FILE *fpi, *fpo; 
 
#define MAX_MEMORY_SIZE  1024 
struct { 
        unsigned int  nAddress; 
        unsigned int  byData; 
} Memory[MAX_MEMORY_SIZE]; 
 
char szLine[80]; 
unsigned int  start_address, address, ndata_bytes, ndata_words; 
unsigned int  data; 
unsigned int  nMemoryCount; 
char *MakeBinaryString (unsigned int data); 
 
char *szHelpLine = 
"\nThe Synthetic PIC --- HEX File to VHDL Memory Entity conversion." 
"\nUsage: HEX2VHDL <filename>" 
"\n  Input file is assumed to have the extension 'hex'." 
"\n  Output will go to the filename with the extension 'vhd'.\n\n"; 
 
char szInFilename[40]; 
char szOutFilename[40]; 
 
int main (int argc, char *argv[]) 
{ 
        int  i; 
        if (!(argc == 2 || argc == 3)) { 
                printf (szHelpLine); 
                exit(1); 
        } 
        if ( (strcmp(argv[1], "?") == 0) || 
                  (strcmp(argv[1], "help") == 0) || 
                  (strcmp(argv[1], "-h") == 0) || 
                  (strcmp(argv[1], "-?") == 0)) { 
                printf (szHelpLine); 
                exit(1); 
        } 
        strcpy (szInFilename, argv[1]); 
        if ( strstr(szInFilename, ".") == 0) 
                strcat (szInFilename, ".hex"); 
 
        strcpy (szOutFilename, argv[1]); 
        strcat (szOutFilename, ".vhd"); 
 
        if((fpi=fopen(szInFilename, "r"))==NULL){ 
                printf("Can\'t open file %s.\n", szInFilename); 
                exit(1); 
        } 
        nMemoryCount = 0; 
        while (!feof(fpi)) { 
                fgets (szLine, 80, fpi); 
                if (strlen(szLine) >= 10) {
                        sscanf (&szLine[1], "%2x%4x", &ndata_bytes, &start_address); 
                        if (start_address >= 0 && start_address <= 20000 && ndata_bytes > 0) { 
                                i = 9; 
                                ndata_words   = ndata_bytes/2; 
                                start_address = start_address/2; 
                                for (address = start_address; address < start_address + ndata_words; address++) { 
                                        sscanf (&szLine[i], "%04x", &data); 
                                        data = ((data >> 8) & 0x00ff) | ((data << 8) & 0xff00); 
                                        i += 4; 
                                        Memory[nMemoryCount].nAddress = address; 
                                        Memory[nMemoryCount].byData   = data; 
                                        nMemoryCount++; 
                                } 
                        } 
                } 
        } 
        if((fpo=fopen(szOutFilename, "w"))==NULL){ 
                printf("Can't open VHDL file '%s'.\n", szOutFilename); 
                exit(1); 
        } 
        fprintf (fpo, "\nlibrary IEEE;"); 
        fprintf (fpo, "\nuse IEEE.std_logic_1164.all;"); 
        fprintf (fpo, "\n--use IEEE.std_logic_arith.all;"); 
        fprintf (fpo, "\nuse IEEE.numeric_std.all;"); 
        fprintf (fpo, "\n\nentity PIC_ROM is"); 
        fprintf (fpo, "\n  port ("); 
        fprintf (fpo, "\n        Addr    : in   std_logic_vector(10 downto 0);"); 
        fprintf (fpo, "\n        Data    : out  std_logic_vector(11 downto 0));"); 
        fprintf (fpo, "\nend PIC_ROM;"); 
        fprintf (fpo, "\n\n\narchitecture first of PIC_ROM is"); 
        fprintf (fpo, "\nbegin"); 
        fprintf (fpo, "\n  Data <= "); 
        for (i = 0; i < nMemoryCount; i++) { 
                fprintf (fpo,"\n        \"%s\" When to_integer(unsigned(Addr)) = %04d Else", 
                        MakeBinaryString(Memory[i].byData)+4,    
                        Memory[i].nAddress 
                ); 
        } 
        fprintf (fpo, "\n        \"000000000000\";"); 
        fprintf (fpo, "\nend first;"); 
        fclose (fpi); 
        fclose (fpo); 
} 
 
char *MakeBinaryString (unsigned int data) 
{ 
        static char szBinary[20];  
        int  i; 
        for (i = 0; i < 16; i++) { 
                if (data & 0x8000) { 
                        szBinary[i] = '1'; 
                } 
                else { 
                        szBinary[i] = '0'; 
                } 
                data <<= 1; 
        } 
        szBinary[i] = '\0'; 
        return szBinary; 
}

Remarque

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.


[modifier] 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.

Remarque

Depuis la rédaction de cette conclusion, j'ai 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
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.

[modifier] Remarque finale

Nous allons classer ce chapitre comme terminé alors qu'il y aurait bien des choses à encore explorer.

Remarque finale

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 softprocesseurs. 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.


[modifier] Voir aussi


Very High Speed Integrated Circuit Hardware Description Language
bouton image vers le chapitre précédent Micro contrôleur embarqué : le PicoBlaze


Outils personnels
Espaces de noms

Variantes
Actions
Navigation
Communiquer
Contribuer
Imprimer / exporter
Boîte à outils