Aller au contenu

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

Leçons de niveau 15
Une page de Wikiversité, la communauté pédagogique libre.
Début de la boite de navigation du chapitre
Système monopuce compatible avec les PIC 16C57
Icône de la faculté
Chapitre no 8
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
En raison de limitations techniques, la typographie souhaitable du titre, « Very High Speed Integrated Circuit Hardware Description Language : Système monopuce compatible avec les PIC 16C57
Very High Speed Integrated Circuit Hardware Description Language/Système monopuce compatible avec les PIC 16C57
 », n'a pu être restituée correctement ci-dessus.

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.


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

[modifier | modifier le wikicode]

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

[modifier | modifier le wikicode]

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.

Le jeu d'instructions du 16C57

[modifier | modifier le wikicode]

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.

Mémoire ROM du 16C57

[modifier | modifier le wikicode]

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

[modifier | modifier le wikicode]

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

[modifier | modifier le wikicode]

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

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

[modifier | modifier le wikicode]

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.

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;


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.

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

[modifier | modifier le wikicode]
Avant d'aller plus loin, relisez Interfaces VGA et PS2.

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

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.

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

[modifier | modifier le wikicode]

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

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.

Utiliser le Timer0 avec le langage C

[modifier | modifier le wikicode]

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


Programme complet de gestion simple de la balle

[modifier | modifier le wikicode]

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 {{Unité|2|bits}} 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.

Annexe 1 : transformer un fichier hex en fichier VHDL

[modifier | modifier le wikicode]

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; 
}


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.


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

[modifier | modifier le wikicode]

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.