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
[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.
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
[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
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
[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.
La RAM
[modifier | modifier le wikicode]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;
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
[modifier | modifier le wikicode]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]À 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 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.
Exercice 1
[modifier | modifier le wikicode]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.
library ieee;
use ieee.std_logic_1164.all;
ENTITY TopTopLogic IS
port (
MCLK: in std_logic;
PCOUT0: out std_logic_vector( 7 downto 0 );
PCOUT1: 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 );
PTSTB0: out std_logic;
PTSTB1: out std_logic;
PTSTB2: out std_logic;
RESET: in std_logic;
SLEEP: out std_logic;
TMRCLK: in std_logic;
affpdsfaible,affpdsfort : out std_logic;
sorties : out std_logic_vector(6 downto 0);
hsynch,vsynch,red,green,blue : out STD_LOGIC
);
END TopTopLogic;
ARCHITECTURE aTopTopLogic OF TopTopLogic IS
signal EADR: std_logic_vector(6 downto 0 );
signal EALU: std_logic_vector(7 downto 0 );
signal EMCLK_16: std_logic;
signal EPRC: std_logic_vector(10 downto 0 );
signal EWERAM: std_logic;
signal GP: std_logic_vector(7 downto 0 );
signal PRESET: std_logic;
signal MCLK_16 : std_logic;
signal ROM: std_logic_vector(11 downto 0 );
COMPONENT VGAtop IS
PORT (clk_50 : in STD_LOGIC;
x_rect, y_rect: IN std_logic_vector(9 DOWNTO 0);
y_raquG, y_raquD: IN std_logic_vector(7 DOWNTO 0);
hsynch,vsynch,red,green,blue : out std_logic);
END COMPONENT VGAtop;
component xilinx_one_port_ram_sync
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 component xilinx_one_port_ram_sync;
COMPONENT PIC_ROM
port(
addr: in std_logic_vector(10 downto 0);
data: out std_logic_vector(11 downto 0)
);
end COMPONENT PIC_ROM;
component TOPLOGIC
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 component;
signal s_x, s_y : std_logic_vector(9 downto 0);
signal s_PORTB, s_PTOUT2,s_PCOUT2 : std_logic_vector(7 downto 0);
BEGIN
i0b: VGAtop PORT MAP (clk_50 => MCLK,
hsynch=>hsynch,vsynch=>vsynch, red=>red,green=>green,blue=>blue, y_raquG =>s_PTOUT2,
y_raquD=>s_PCOUT2, x_rect=>s_x, y_rect=>s_y);
i1: PIC_ROM PORT MAP (addr =>EPRC, data => ROM);
i2:xilinx_one_port_ram_sync PORT MAP(
clk=>MCLK,
we=>EWERAM,
addr=>EADR,
din=>EALU ,
dout=>GP );
i3: TOPLOGIC PORT MAP (
TESTIN=>'0',
EADR=>EADR,
EALU=>EALU,
EMCLK_16=>EMCLK_16,
EPRC=>EPRC,
EWERAM=>EWERAM,
GP=>GP,
MCLK=>MCLK,
PCOUT0(7 downto 2)=>PCOUT0(7 downto 2),
PCOUT0(1 downto 0)=>s_x(9 downto 8),
PCOUT1(7 downto 2)=>PCOUT1(7 downto 2),
PCOUT1(1 downto 0)=>s_y(9 downto 8),
PCOUT2=>s_PCOUT2,
PTIN0=>PTIN0,
PTIN1=>PTIN1,
PTIN2=>PTIN2,
PTOUT0=>s_x(7 downto 0),
PTOUT1=>s_y(7 downto 0),
PTOUT2=>s_PTOUT2,
PTSTB0=>PTSTB0,
PTSTB1=>PTSTB1,
PTSTB2=>PTSTB2,
PRESET=>'0',--PRESET,
RESET=>RESET,
ROM=>ROM,
SLEEP=>SLEEP,
TMRCLK => TMRCLK
);
-- sorties
PTOUT1 <= s_y(7 downto 0);
PTOUT0 <= s_x(7 downto 0);
END aTopTopLogic ;
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 :
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 :
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".
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.
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
[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;
}
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
[modifier | modifier le wikicode]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é.
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.
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.
Voir aussi
[modifier | modifier le wikicode]- Le rapport complet du projet peut être trouvé en format pdf.
- Le langage C chez Wikipédia
- La programmation C et ses exercices chez WikiBook
- Introduction au langage C chez Wikiversité