Very High Speed Integrated Circuit Hardware Description Language/Embarquer un PIC 16F84
Nous allons mettre en œuvre dans ce chapitre un processeur softcore compatible avec le PIC® 16F84 de Microchip. Il s'agit d'un processeur RISC sur 8 bits. Après avoir exploré le processeur original, nous nous concentrerons sur le modèle VHDL du système mono-puce. Ce projet, bien que relativement simple, nécessitera une exploration avancée du cœur pour y réaliser certaines modifications : ajout d'un troisième PORT et ajout du signal "strobe" associé. Ce travail ne sera pas présenté dans ce chapitre.
Le contenu de ce paragraphe peut sembler redondant avec les chapitres silicore1657 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 !
Choix du cœur
[modifier | modifier le wikicode]Il existe au moins trois versions de descriptions de cœur PIC® 16F84 sur Internet. La plus à jour me semble être un projet en Verilog, appelé risc16F84 dans la suite, et disponible sur le site d'opencores avec des mises à jour récentes (2006). Mais ayant des étudiants qui ne pratiquent que VHDL, nous avons décidé d’utiliser le projet CQPIC, plus ancien certes, mais à l'origine de ce cœur risc16F84.
Nous avons déjà eu l’occasion d’utiliser un cœur de processeur 16C57 dans un autre chapitre de ce cours. Cet ancien projet, dénommé silicore1657 dans la suite de ce document, nous a permis d'acquérir un peu d'expérience mise à profit pour le choix du cœur. Il existe en effet un autre cœur VHDL PIC16F84, appelé PPX par la suite, de Daniel Wallner qui a été utilisé et débogué par Patrice Nouel, Maître de Conférence à l’ENSEIRB (à la retraite maintenant). Mais mon choix s'est porté sur le CQPIC car il n'utilise pas de PORT bidirectionnel mais décompose les PORTS (comme expliqué plus loin dans ce document). La documentation du Silicore1657 nous a alerté à ce sujet : elle attire l'attention de l’utilisateur sur l’utilisation des PORTs dans un cœur. Un cœur bien réalisé doit proposer tout sauf la ROM la RAM et les PORTS parce que leur implémentation est trop dépendante du fabricant du FPGA ciblé. Il n'y a, en effet, aucune norme pour implanter ceux-ci.
Le CQPIC est un cœur de processeur (ou processeur softcore) compatible avec le PIC® 16F84 de microchip. On pouvait le trouver sur Internet sous la forme d'un fichier "cqpic100d.zip". Comme il devient difficile de le trouver nous avons mis une version (améliorée ?) sur Internet : CQPICStart.zip. Il a été développé par Sumio Morioka (Japon) et publié en décembre 1999 dans "Transistor Gijutsu Magazine" (sa dernière mise à jour datait de 2004 avant la notre en 2010)
L'hébergement de mon site perso se termine le 5 septembre 2023 ! A ce jour je n'ai pas encore décidé comment je vais gérer ce problème dont je ne suis pas à l'origine. Il en résulte que le lien CQPICStart et l'ensemble des corrections qui utilisent mon site perso seront indisponibles à partir de cette date. SergeMoutou (discuter) |
Nous allons commencer par présenter la partie hardware du cœur en nous basant sur la documentation de Microchip. Vous pouvez lire aussi comment démarrer avec un PIC16F84 ainsi qu'un cours complet de la wikiversité dédié à la programmation des 16F et 18F avec un peu d'assembleur et beaucoup de C.
Architecture du PIC® 16F84
[modifier | modifier le wikicode]Il s'agit d'un processeur 8 bits avec des instructions codées avec un opcode sur 14 bits. La version originale de ce PIC avait peu de RAM (68 octets). Une autre de ses limitations est la taille de sa pile : avec une pile de huit niveaux seulement, il doit être difficile de réaliser un compilateur C.
Ce processeur gère un certain nombre d'interruptions.
Nous allons commencer par présenter le plus difficile, l'architecture des registres mémoire (Register File dans la terminologie anglo-saxonne que l’on pourrait traduire par dossier/classeur de registres (j'ai trouvé aussi fichier de registres), mais nous préférons registres mémoire ou mémoire de registres ou banc de registres). Nous parlons de difficultés parce qu’il y a plusieurs banques mémoires ce qui est une caractéristique des architectures 8 bits Microchip (jusqu'aux séries 18FXXX) qui disparaissent seulement avec les architectures 16/32 bits (24FXXX et autres)
Architecture des registres SFR et de la mémoire
[modifier | modifier le wikicode]Les registres SFR (Registres internes à Fonctions Spéciales) sont répartis en deux banques. Cette partie de mémoire est suivie par 68 registres d’usage général pouvant être utilisés comme mémoire simple qui sont identiques sur les deux banques. Ensuite vient une série de registres généraux qui eux dépendent de la banque mémoire choisie et qui ne sont pas implémenté dans le 16F84 original.
La mémoire du 16F84 est organisée en banques sélectionnées par le(s) bit(s) RP0 (et RP1) du registre STATUS. Seul RP0 est utilisé pour le 16F84, soit deux banques.
- RAM et Registres du 16F84
Adr. Banque0 Banque1 Adr. 00h Indirect addr. Indirect addr. 80h 01h TMR0 OPTION_REG 02h PCL PCL 82h 03h STATUS STATUS 04h FSR FSR 84h 05h PORTA TRISA 06h PORTB TRISB 86h 07h -- -- 08h EEDATA EECON1 88h 09h EEADR EECON2 0Ah PCLATH PCLATH 8Ah 0Bh INTCON INTCON 0Ch – 4Fh 68 cases mémoires idem banque 0 8Ch – CFH 50h – 7Fh inutilisé inutilisé D0H – FFH
La première partie de cette mémoire (les registres) est parfois appelée banc de registres (ou fichier de registres). La partie marquée inutilisée est naturellement inutilisée dans le PIC16F84 original mais sera en fait utilisée par notre cœur CQPIC. Remarquez aussi l'adresse 07h et 87h inutilisée dans le PIC16F84 mais utilisée par notre cœur comme PORTC.
Vous pouvez lire aussi comment démarrer avec un PIC16F84 et/ou Utiliser les PIC 16F et 18F.
Les instructions
[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.
Il est temps de présenter maintenant l’ensemble des 35 instructions du PIC® 16F84 codées sur 14 bits. 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 11 bits)
- PC compteur programme
Opcode (binary) | Mnemonic | Description |
---|---|---|
00 0000 0xx0 0000 |
NOP | Pas d'opération |
00 0000 0110 0011 |
SLEEP | arrête le processeur |
00 0000 0110 0100 |
CLRWDT | Reset du timer watchdog |
00 1000 dfff ffff |
MOVF f,d | Recopie de W dans f (adressage direct) |
00 0000 1fff ffff |
MOVWF | déplacement de W vers f |
00 0001 0xxx xxxx |
CLRW | Positionne W à 0 (idem à CLR x, W) |
00 0001 1fff ffff |
CLRF f | Positionne f à 0 (idem à CLR f, F) |
00 0010 dfff ffff |
SUBWF f, d | Soustrait W de (d = f − W) |
00 0011 dfff ffff |
DECF f, d | Décrément f (d = f − 1) |
00 0100 dfff ffff |
IORWF f, d | OU Inclusif W avec F (d = f OR W) |
00 0101 dfff ffff |
ANDWF f, d | ET entre W et F (d = f AND W) |
00 0110 dfff ffff |
XORWF f, d | OU Exclusif W avec F (d = f XOR W) |
00 0111 dfff ffff |
ADDWF f, d | Additionne W avec F (d = f + W) |
00 1000 dfff ffff |
MOVF f, d | recopie F (d = f) |
00 1001 dfff ffff |
COMF f, d | Complement f (d = NOT f) |
00 1010 dfff ffff |
INCF f, d | Incrément f (d = f + 1) |
00 1011 dfff ffff |
DECFSZ f, d | Decrément f (d = f − 1) et saut si zero |
00 1100 dfff ffff |
RRF f, d | Rotation droite F (rotation droite avec la retenue) |
00 1101 dfff ffff |
RLF f, d | Rotation gauche F (rotation gauche avec la retenue) |
00 1110 dfff ffff |
SWAPF f, d | échange de groupes 4-bit de f (d = lsb:f msb:f) |
00 1111 dfff ffff |
INCFSZ f, d | Incrément f (d = f + 1) et saut si zero |
01 00bb bfff ffff |
BCF f, b | RaZ d'un bit de f (Clear bit b of f) |
01 01bb bfff ffff |
BSF f, b | Mise à 1 d'un bit de f (Set bit b of f) |
01 10bb bfff ffff |
BTFSC f, b | test de bit de f, saute si zéro (Test bit b of f) |
01 11bb bfff ffff |
BTFSS f, b | test de bit de f, saute si un (Test bit b of f) |
11 01xx kkkk kkkk |
RETLW k | Positionne W à k et retour |
10 0kkk kkkk kkkk |
CALL k | Sauve l'adresse de retour, charge PC avec k |
00 0000 0000 1001 |
RETFIE | retour d'interruption |
00 0000 0000 1000 |
RETURN | retour de sous-programme |
10 1kkk kkkk kkkk |
GOTO k | saut à l'adresse k (9 bits!) |
11 111x kkkk kkkk |
ADDLW k | Addition de W et k |
11 110x kkkk kkkk |
SUBLW k | Soustraction de W et k |
11 00xx kkkk kkkk |
MOVLW k | Chargement littéral de W (W = k) |
11 1000 kkkk kkkk |
IORLW k | OU Inclusif littéral avec W (W = k OR W) |
11 1001 kkkk kkkk |
ANDLW k | ET littéral avec W (W = k AND W) |
11 1010 kkkk kkkk |
XORLW k | OU exclusif or littéral avec W (W = k XOR W) |
Dans la terminologie Microchip, littéral signifie en adressage immédiat.
Vous pouvez lire aussi comment démarrer avec un PIC16F84 et/ou Utiliser les PIC 16F et 18F.
Architecture matérielle du PIC 16F84
[modifier | modifier le wikicode]L'architecture matérielle du PIC 16F84 est relativement simple : elle peut être présentée sous forme de schéma.
Évidemment, une description matérielle de cette architecture en VHDL laisse de côté un certain nombre de fonctions comme la réalisation d'une horloge, la gestion analogique des ports avec la possibilité de résistances de tirages par exemple …
Notre cœur ou processeur softcore
[modifier | modifier le wikicode]Le cœur original CQPIC a été réalisé de manière à être le plus proche du composant PIC® 16F84 original. Voici maintenant une description du cœur sous forme de programme VHDL.
component piccore
generic (
STACK_SIZE : integer;
WDT_SIZE : integer
);
port (
progdata : in std_logic_vector(13 downto 0);
progadr : out std_logic_vector(12 downto 0);
ramdtin : in std_logic_vector(7 downto 0);
ramdtout : out std_logic_vector(7 downto 0);
ramadr : out std_logic_vector(8 downto 0);
readram : out std_logic;
writeram : out std_logic;
existeeprom : in std_logic;
eepdtin : in std_logic_vector(7 downto 0);
eepdtout : out std_logic_vector(7 downto 0);
eepadr : out std_logic_vector(7 downto 0);
readeepreq : out std_logic;
readeepack : in std_logic;
writeeepreq : out std_logic;
writeeepack : in std_logic;
--> changed ver1.10a, 2010/05/13 (Serge moutou)
-- porta_in : in std_logic_vector(4 downto 0);
-- porta_out : out std_logic_vector(4 downto 0);
-- porta_dir : out std_logic_vector(4 downto 0);
porta_in : in std_logic_vector(7 downto 0);
porta_out : out std_logic_vector(7 downto 0);
porta_dir : out std_logic_vector(7 downto 0);
--<
portb_in : in std_logic_vector(7 downto 0);
portb_out : out std_logic_vector(7 downto 0);
portb_dir : out std_logic_vector(7 downto 0);
rbpu : out std_logic;
--> added ver1.10b May 22 2010 (Serge Moutou)
portc_in : in std_logic_vector(7 downto 0); -- PORT-C input data
portc_out : out std_logic_vector(7 downto 0); -- PORT-C output data
portc_dir : out std_logic_vector(7 downto 0); -- TRISC: PORT-C signal direction (H:input, L:output)
portc_strobe: out std_logic; -- PORT_C strobe
--<
int0 : in std_logic;
int4 : in std_logic;
int5 : in std_logic;
int6 : in std_logic;
int7 : in std_logic;
t0cki : in std_logic;
wdtena : in std_logic;
wdtclk : in std_logic;
wdtfull : out std_logic;
powerdown : out std_logic;
startclkin : out std_logic;
ponrst_n : in std_logic;
mclr_n : in std_logic;
clkin : in std_logic;
clkout : out std_logic
);
end component;
À noter que contrairement au PIC® 16F84, le composant CQPIC utilise une entrée spécifique "t0cki" pour le timer0 (pour le PIC® c’est le bit b4 T0CKl du PORTA). Il y a encore bien d'autres différences : comparez au brochage original du PIC® 16F84 décrit dans un autre projet qui fait apparaître 18 broches seulement.
En général un processeur softcore comprend toujours beaucoup plus d'entrées sorties que le processeur original. Une des raisons est que le cœur donné ci-dessus ne comprend pas sa RAM et sa ROM contrairement au composant original.
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 deux ports du CQPIC
[modifier | modifier le wikicode]Nous avons fait des modifications sur la gestion des PORTS dans le cœur original qui seront présentées plus loin. Nous nous contenterons dans cette section de généralités.
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 (du FPGA) et donc de l'environnement d'utilisation. Ainsi, la description VHDL des ports est laissée à l'utilisateur du cœur CQPIC.
Un coup d'œil sur le cœur nous montre que chaque port est décomposé en trois ports distincts. Par exemple, pour le PORTA on trouve porta_dir, porta_in et porta_out. Cette façon de faire est assez courante dans les "soft core" et j’ai déjà eu l’occasion de la rencontrer lors d'un projet précédent, le silicore1657. La bidirectionnalité des PORTs 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 porta_dir et portb_dir. Ils sont tous les deux sur 8 bits contrairement au PIC® 16F84 pour lequel le PORTA ne possède que 5 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 porta_in et portb_in ainsi qu'en ports de sortie porta_out et portb_out.
Pour des raisons de compatibilité le port porta_dir n’est pas accessible de manière normale. L'écriture dans porta_dir se fait par l'instruction :
// en langage C
TRISA=0xFF; // 1 ⟺ input
tandis qu'en assembleur on écrira :
; assembleur bsf STATUS,5 ; select memory bank 1 movlw 255 ;1:input movwf TRISA ;
Comme deuxième remarque, nous dirons qu’à l'origine le PORTA ne disposait que de 5 bits et que nous avons modifié le cœur et le fichier CQPIC.VHD pour que l’on puisse l’utiliser sur 8 bits. Tous les fichiers correspondants se trouvent dans le répertoire "\CQPICStart\PORTA8" du fichier CQPICStart.zip.
Notre dernière remarque sera pour compléter notre description. Si l’on écrit dans un programme C :
// en langage C
PORTA = PORTA; // recopie d'un PORT
ceci aura comme signification : porta_out <= porta_in. Physiquement, ce qui est à gauche du signe égal, n’est pas la même chose que ce qui est à droite.
La description des PORTs dans cette section n’est pas complète et sera reprise quand nous aurons besoin de les relier à une logique externe.
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]Il existe peu de modèles de mémoire standard dans le monde des FPGA et ASIC. En fait le type le plus commun de mémoire que l’on peut trouver est ce que la norme WISHBONE appelle 'FASM', ou FPGA and ASIC Subset Model. Pour la RAM, sa spécificité est qu’il s'agit d'une mémoire à écriture et lecture synchrone. 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 dataram is
generic(
ADDR_WIDTH: integer:=9;
DATA_WIDTH: integer:=8
);
port(
clk: in std_logic;
write: in std_logic;
read: in std_logic;
addr: in std_logic_vector(ADDR_WIDTH-1 downto 0);
datain: in std_logic_vector(DATA_WIDTH-1 downto 0);
dataout: out std_logic_vector(DATA_WIDTH-1 downto 0)
);
end dataram;
architecture RTL of dataram 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 (write='1') then
ram(to_integer(unsigned(addr))) <= datain;
end if;
addr_reg <= addr;
end if;
end process;
dataout <= ram(to_integer(unsigned(addr_reg)));
end RTL;
La gestion de la RAM dans un système mono-puce n’est pas un problème aussi simple qu’il n'y parait. Examinons de plus près ce problème et ce qui fait sa difficulté.
Un coup d'œil sur le programme ci-dessus nous indique immédiatement que les adresses mémoires sont sur 9 bits alors que la documentation du PIC16F84 donne des plages inutilisées à partir de FFh soit sur 8 bits. En VHDL, donner plus de mémoire à un cœur se fait très facilement s'il y a de la place dans le FPGA pour cela. Comme nous avons décidé d’utiliser un compilateur C, notre problème est de savoir comment faire comprendre au compilateur C que l’on dispose de plus de mémoire que prévu ?
Normalement les compilateurs ont une directive de compilation pour cela qu’il n’est pas difficile de trouver en page 60 de la documentation du compilateur HiTech C.
Aller dans :
Project → Build Options → project →onglet "global" et remplir "RAM Range".
Sans directive on obtient :
Data space used 7h ( 7) of 44h bytes ( 10.3%)
Avec "default,+50-7f" on obtient :
Data space used 7h ( 7) of 74h bytes ( 6.0%)
Avec "default,+50-7f,+d0-ff" on obtient :
Data space used 7h ( 7) of A4h bytes ( 4.3%)
ce qui montre que l’on peut effectivement augmenter l'espace géré par le compilateur. Nous avons réussi à gérer toutes les adresses sur 8 bits. Il nous reste un 9e bit à gérer. Mais cela nécessite forcément d'aller regarder dans le cœur comment tout cela est géré, ce que nous n'avons pas fait pour le moment. Nous pensons de toute façon qu’il faut changer de PIC pour prendre un PIC16F87X dans notre projet C, mais ceci n’est pas sans risque puisqu’il y a alors quatre banques et que certain des registres ont été déplacés.
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.
-- This file was generated with hex2rom written by Daniel Wallner
library IEEE;
use IEEE.std_logic_1164.all;
use IEEE.numeric_std.all;
entity progrom is
port(
Clk : in std_logic;
romaddr : in std_logic_vector(12 downto 0);
romout : out std_logic_vector(13 downto 0)
);
end progrom;
architecture rtl of progrom is
signal A_r : std_logic_vector(12 downto 0);
begin
process (Clk)
begin
if Clk'event and Clk = '1' then
A_r <= romaddr;
end if;
end process;
process (A_r)
begin
case to_integer(unsigned(A_r)) is
when 000000 => romout <= "00000100101000"; -- 0x0000
when 000001 => romout <= "00001100010110"; -- 0x0002
when 000002 => romout <= "11111100110000"; -- 0x0004
when 000003 => romout <= "00010100000000"; -- 0x0006
when 000004 => romout <= "00001100010000"; -- 0x0008
when 000005 => romout <= "00000000110000"; -- 0x000A
when 000006 => romout <= "00001100011000"; -- 0x000C
when 000007 => romout <= "00000100110000"; -- 0x000E
when 000008 => romout <= "00011000000000"; -- 0x0010
when 000009 => romout <= "00001100010010"; -- 0x0012
when 000010 => romout <= "00010100001000"; -- 0x0014
when 000011 => romout <= "00011000000000"; -- 0x0016
when 000012 => romout <= "00100100101000"; -- 0x0018
when 000013 => romout <= "00000000101000"; -- 0x001A
when others => romout <= "--------------";
end case;
end process;
end;
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 se faire automatiquement. Nous utiliserons un programme en C++ que nous donnons en annexe 1 et qui se trouve dans le répertoire "\CQPICStart\ROM" du fichier CQPICStart.zip.
Nos premiers programmes en C
[modifier | modifier le wikicode]Nous n'allons pas présenter le langage C maintenant puisque d'autres projets le font :
- Le langage C chez Wikipédia
- La programmation C et ses exercices chez WikiBook
- Introduction au langage C et les PIC 16F et 18F en assembleur et C chez Wikiversité
La programmation du cœur se fait à l'aide de l'environnement MPLAB. Ce n’est pas le seul environnement de développement pour les PICs, mais c’est probablement un des plus utilisés et pour ne rien gâcher, il est gratuit. Le téléchargement de MPLAB nous permet d’utiliser l'assembleur ainsi que le compilateur C HiTech. Puisque nous avons décidé pour ce projet de n'utiliser que des outils gratuits, le compilateur C n’est pas optimisé, pour cela il faut payer.
MPLAB fonctionne sous wine donc sous Linux contrairement à winAVR.
MPLABX fonctionne directement sous Linux (sans wine) et naturellement sous Windows.
Notre premier programme en C a été réalisé pour tester le fonctionnement du cœur CQPIC sur notre carte d'application Digilent.
Notre architecture CQPIC étant une architecture 8 bits avec peu de mémoire RAM, il peut sembler intéressant de commencer par présenter un programme en C mais qui ne contient que de l'assembleur.
Le premier programme simple en C avec assembleur
[modifier | modifier le wikicode]Le programme présenté montre comment inclure de l'assembleur dans un programme C.
#include <pic1684.h>
//#include <htc.h> serait-il mieux ?
main(void)
{
#asm
Start: bcf _STATUS,5 ; select memory bank 0
movf _PORTA, W ;read PORTA
bsf _STATUS,5 ; select memory bank 1
movwf _TRISA ;Recopie de PORTA
goto Start
#endasm
}
Ne prenez pas ce programme à la lettre. Il est donné pour exemple mais fonctionne de manière surprenante.
- Pour avoir d'autres exemples en assembleur, consultez le WIKIBOOK Comment démarrer avec un PIC 16F84.
Le premier programme simple en C pur
[modifier | modifier le wikicode]Un programme tout simple de test est présenté maintenant : il s'agit tout simplement de recopier le PORTA sur le PORTB. Remarquons aussi que ce programme fonctionne complètement si l’on a pris soin de prendre la version que j’ai réalisée.
#include <pic1684.h>
//#include <htc.h> serait-il mieux ?
main(void)
{ // La gestion de TRISA et TRISB semble importante dans ce cœur
TRISA = 0xFF; // 8 entrees pour A
TRISB = 0x00; // 8 sorties pour B
while(1)
PORTB = PORTA; // recopie du PORTA dans le PORTB qui allume les LEDs
}
Contrairement au Silicore1657 déjà évoqué (projet de l'année précédente), le cœur CQPIC dispose du mécanisme d'interruption que nous allons détailler maintenant avec un exemple simple.
Interruption
[modifier | modifier le wikicode]Pour tester plus en avant notre cœur CQPIC, je décide d’utiliser une interruption. La plus simple des interruptions à mettre en œuvre étant celle du timer0, nous allons examiner maintenant la documentation correspondante.
La documentation du timer0
[modifier | modifier le wikicode]Nous présentons maintenant le schéma complet correspondant à ce timer0.
Comme on peut le voir dans la documentation schématique, la gestion du timer0 est essentiellement réalisée par le registre OPTION. Seuls les trois bits de poids faibles de ce registre sont à positionner à 1 pour avoir une division par 256. Comme on le verra dans le programme, cette division ne suffit pas il faudra encore lui ajouter une division par 16 dans l'interruption même, pour que notre chenillard soit visible.
Mettre en route le timer0 est une chose, mais déclencher une interruption en est une autre. Il nous faut maintenant documenter ce mécanisme.
Pour réaliser l'interruption, il faut réaliser un front montant dans l'ellipse rouge : on en déduit immédiatement les bits à mettre à 1 dans INTCON, à savoir T0IE et GIE.
Vous trouverez un peu plus de détails dans un autre projet sur le timer 0 et son interruption. Les schémas internes utilisés sont les mêmes, mais avec un peu plus de commentaires.
Le programme
[modifier | modifier le wikicode]Le programme correspondant est donné maintenant.
#include <pic1684.h>
void interrupt decalage(void);
unsigned char nb;
main(void)
{
TRISA = 0xF9; // 6 entrees, 2 sorties pour A
TRISB = 0x00; // 8 sorties pour B
OPTION = 0x07; // prescaler 256 , entree sur quartz
INTCON = 0xA0; // autorise l'interruption timer
PORTB = 0x01; // une seule diode allumee
TMR0 = 0x00 ;
nb=0;
while(1)
{
// on ne fait rien que recopier sur 2 segments la valeur de SW1
if ((PORTA & 0x01) == 1) PORTA = 0x06;
}
}
void interrupt decalage(void)
{
nb++;
//TMR0 = 0x00; //c'est fait car overflow
if (!(nb % 16))
PORTB = (PORTB << 1) ;
if (PORTB == 0x00) PORTB = 0x01;
T0IF = 0; // acquittement interruption
}
Remarquez le "if (!(nb % 16))" qui réalise la division par 16 dans l'interruption. C'est absolument nécessaire pour notre cœur qui fonctionne à 50 Mhz pour voir les LEDs allumées se déplacer.
Le lecteur, fin programmeur, nous fera remarquer, à juste titre, que la division est par 16. Il s'agit donc d'une division par une puissance de 2. Le calcul du reste peut alors se faire avec un masque. C'est certainement plus optimisé que l'opérateur modulo mais le projet de pong ne nécessite pas ce genre d'optimisation : notre programme passe son temps à attendre !
Il est maintenant grand temps de revenir sur notre problème original, à savoir interfacer un écran VGA.
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.
Le problème des PORTs
[modifier | modifier le wikicode]Nous avons besoin de deux fois 10 bits pour piloter nos coordonnées de balles. Une autre façon de dire les choses est qu’il nous faut 4 ports de 8 bits en sortie. Or, nous ne disposons que de deux PORTs de 8 bits en sortie (et encore parce que j’ai étendu le PORTA originel. C'est insuffisant. Il nous faut donc réfléchir sur la façon dont on va pouvoir connecter toutes ces données en sortie.
Pourquoi seulement deux PORTs en sortie ? L'entité du piccore présentée en section précédente ne montre-t-elle pas porta_dir et portb_dir en sortie ?
La modification du cœur CQPIC pour augmenter le nombre de bits du PORTA, m'a amené à lire son code VHDL et je suis tombé sur :
-- VHDL
for I in 0 to 7 loop
if (trisa_reg(I) = '1') then
ramin_node(I) := portain_sync_reg(I);-- PORT A (when input mode)
else
ramin_node(I) := portaout_reg(I);-- PORT A (when output mode)
end if;
end loop;
Ce code montre que TRISA est pris en compte en interne dans le cœur CQPIC. Ceci peut sembler normal, mais n’est pas conforme à la seule expérience que nous avions avec le Silicore1657 : puisque TRISA était sorti à l'extérieur il pouvait servir de PORT supplémentaire. Si vous avez l’idée d’utiliser TRISA comme PORT supplémentaire ici, vous aurez un fonctionnement étrange d'un programme C :
//langage C
TRISA = PORTA ;
Physiquement, on relie PORTA (ra_out) à des interrupteurs et TRISA (ra_dir) à des LEDs. Si PORTA est à OxFF (par les interrupteurs extérieurs), tout interrupteur mis à 0 éteindra correctement la LED correspondante (reliée à TRISA). Mais l'inverse ne marche plus : si ce même interrupteur est remis à 1 on n'allume plus la LED correspondante.
Les deux PORTs TRIS (physiquement porta_dir et portb_dir) du cœur CQPIC ne peuvent en aucun cas être utilisés comme PORTs supplémentaires de sortie. Ils sortent physiquement du cœur mais ne peuvent être utilisés sans précautions.
Après plusieurs années d'expérience avec les processeurs embarqués depuis que ce chapitre a été écrit, nous décidons de compléter cette conclusion. Nous ne comprenons absolument pas ce qui pousse les auteurs de cœurs récents à recopier la bidirectionnalité des PORTs (CQPIC n’est pas récent 1999-2002). Un FPGA comporte aujourd’hui bien plus de broches qu'un micro-contrôleur vendu dans le commerce. Ainsi tous les cœurs récents ne devraient pas utiliser la haute impédance en séparant les entrées et sorties. TRISA, TRISB et TRISC deviendraient alors tout naturellement des registres (ou des PORTs) !
Comment contourner cet obstacle ?
Ajouter un PORTC dans le CQPIC
[modifier | modifier le wikicode](Le cœur correspondant se trouve dans le répertoire "\CQPICStart\PORTC" du fichier CQPICStart.zip).
Après avoir passé plusieurs heures à étendre à 8 bits le PORTA (au lieu des 5 initiaux), nous avons repéré à peu près tous les endroits à modifier pour ajouter un PORTC au CQPIC. Le jeu en vaut-il la chandelle ? Cela ne peut pas suffire à résoudre nos problèmes, puisqu'on aura alors en sortie que trois PORTs sur les huit qu’il nous faut. Nous avons finalement réalisé ce changement dans le cœur et en avons profité pour ajouter un signal "strobe" qui accompagne ce port supplémentaire (voir section suivante). C'est ce signal strobe qui va nous permettre de résoudre notre problème d'extension du nombre de PORTs assez facilement.
Notez que cette solution impose de déclarer PORTC et TRISC dans les programmes C. Je déconseille une modification du fichier pic1684.h Faites plutôt comme ceci :
#include <pic1684.h> // ou #include <hct.h>
volatile unsigned char PORTC @ 0x07;
volatile unsigned char TRISC @ 0x87;
C'est simple, mais cela ne permet pas un accès à des bits individuels (mais nous n'avons pas besoin de cela dans notre projet).
Approfondissons cette solution.
Ajouter un "strobe" au PORTC et augmenter le nombre de ports
[modifier | modifier le wikicode]Un signal "strobe" est une sortie qui indique une écriture dans le PORTC. Ajouter ce signal strobe a été en fait le plus délicat du travail. L'intérêt d'un tel signal est de permettre facilement la démultiplication des PORTs (jusqu'à 256). Nous présentons ci-dessous un exemple qui montre comment étendre à huit le nombre de PORTs. On met la valeur du port véritable de destination dans PORTA puis on met dans PORTC la valeur à écrire : le "strobe" lui sera alors envoyé et il recopiera ce qu’il y a dans PORTC.
Il est peut être plus facile de raisonner à partir de ce schéma. Le PORTC (a gauche) est relié à tous les ports ajoutés (à droite) et la logique de décodage envoie le strobe dans le port qui doit recopier ce qu’il y a dans PORTC.
Le fait que le cœur original ne gère pas de signal "strobe" est un grand handicap si l’on veut ajouter de la logique externe. C'est une des raisons qui nous ont fait nous décider à ajouter un PORTC et son strobe associé. Nous ne pensons pas que ce travail puisse être réalisé par un étudiant du niveau 14.
Le programme VHDL correspondant au schéma de la page précédente est présenté maintenant. Il comporte deux entrées PORTA et PORTC pour réaliser quatre ports de sorties pA, pB, pC, pD.
-- increase the number of available ports with the new portC and its strobe
-- ver1.10b, 2010/05/22 (Serge Moutou)
library ieee;
use ieee.std_logic_1164.all;
entity ports is
port(
clk : in std_logic;
strobeC_in : in std_logic;
portA : in std_logic_vector(7 downto 0);
portC : in std_logic_vector(7 downto 0);
pA,pB,pC,pD : out std_logic_vector(7 downto 0) --new ports
);
end ports;
architecture BehPorts of ports is
signal Internal_strobes : std_logic_vector(7 downto 0);
begin
dmux_Strobe:process(portA,strobeC_in) begin
case portA is
when "00000001" => Internal_strobes(0) <= strobeC_in;
when "00000010" => Internal_strobes(1) <= strobeC_in;
when "00000100" => Internal_strobes(2) <= strobeC_in;
when "00001000" => Internal_strobes(3) <= strobeC_in;
when others => internal_strobes <= (others =>'0');
end case;
end process;
port_A:process(clk)begin
if clk'event and clk='1' then
if Internal_strobes="00000001" then
pA<=portC;
end if;
end if;
end process;
port_B:process(clk)begin
if clk'event and clk='1' then
if Internal_strobes="00000010" then
pB<=portC;
end if;
end if;
end process;
port_C:process(clk)begin
if clk'event and clk='1' then
if Internal_strobes="00000100" then
pC<=portC;
end if;
end if;
end process;
port_D:process(clk)begin
if clk'event and clk='1' then
if Internal_strobes="00001000" then
pD<=portC;
end if;
end if;
end process;
end BehPorts;
Remarquez qu'on a utilisé des "process" au lieu de "components" pour simplifier le programme.
Exercice
[modifier | modifier le wikicode]Le programme VHDL ci-dessus permet de gérer 4 ports à partir de deux initiaux : le PORTA et le PORTC. Modifier ce programme VHDL pour qu’il gère les 6 ports en sortie conformément à la figure ci-dessus.
-- increase the number of available ports with the new portC and its strobe
-- ver1.10b, 2010/05/22 (Serge Moutou)
library ieee;
use ieee.std_logic_1164.all;
entity ports is
port(
clk : in std_logic;
strobeC_in : in std_logic;
portA : in std_logic_vector(7 downto 0);
portC : in std_logic_vector(7 downto 0);
pA,pB,pC,pD,pE,pF : out std_logic_vector(7 downto 0) --neww ports
);
end ports;
architecture BehPorts of ports is
signal Internal_strobes : std_logic_vector(7 downto 0);
begin
dmux_Strobe:process(portA,strobeC_in) begin
case portA is
when "00000001" => Internal_strobes(0) <= strobeC_in;
when "00000010" => Internal_strobes(1) <= strobeC_in;
when "00000100" => Internal_strobes(2) <= strobeC_in;
when "00001000" => Internal_strobes(3) <= strobeC_in;
when "00010000" => Internal_strobes(4) <= strobeC_in;
when "00100000" => Internal_strobes(5) <= strobeC_in;
when others => internal_strobes <= (others =>'0');
end case;
end process;
port_A:process(clk)begin
if clk'event and clk='1' then
if Internal_strobes="00000001" then
pA<=portC;
end if;
end if;
end process;
port_B:process(clk)begin
if clk'event and clk='1' then
if Internal_strobes="00000010" then
pB<=portC;
end if;
end if;
end process;
port_C:process(clk)begin
if clk'event and clk='1' then
if Internal_strobes="00000100" then
pC<=portC;
end if;
end if;
end process;
port_D:process(clk)begin
if clk'event and clk='1' then
if Internal_strobes="00001000" then
pD<=portC;
end if;
end if;
end process;
port_E:process(clk)begin
if clk'event and clk='1' then
if Internal_strobes="00010000" then
pE<=portC;
end if;
end if;
end process;
port_F:process(clk)begin
if clk'event and clk='1' then
if Internal_strobes="00100000" then
pF<=portC;
end if;
end if;
end process;
end BehPorts;
Arrivé à ce point, il nous faut certainement donner une image simple 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.
Nous allons traiter ce problème en commençant à gérer la balle seulement puis en ajoutant les raquettes.
Programmation du nouveau cœur avec écran VGA
[modifier | modifier le wikicode](L'ensemble se trouve dans le répertoire "\CQPICStart\VGA_Pong" du fichier CQPICStart.zip). Nous avons déjà présenté quelques programmes en C, mais nous allons examiner ce qu’il faut changer dans ces programmes pour gérer les nouveaux PORTs.
Programmation en C
[modifier | modifier le wikicode]Nous commençons par présenter un sous-programme qui écrit une valeur 16 bits dans deux des nouveaux PORTs.
Le sous-programme "setX"
[modifier | modifier le wikicode]Une version en C pur est montrée ci-dessous :
void setX(unsigned int x){
TRISA=0x00;TRISC=0x00;
PORTA=1;
PORTC=x; //poids faible
PORTA=2;
PORTC=x>>8;//poids fort
}
Le contenu de ce programme est complètement déterminé par la partie matérielle.
Déplacement horizontal en continu de notre balle/rectangle
[modifier | modifier le wikicode]Nous allons présenter maintenant un programme fonctionnel qui déplace sans arrêt le rectangle sur une horizontale.
#include <pic1684.h>
volatile unsigned char PORTC @ 0x07;
volatile unsigned char TRISC @ 0x87;
void setX(unsigned int x);
void setY(unsigned int y);
void wait(int tempo);
void main(){
int i;
while(1){
setY(321);
for(i=0;i<600;i++){
setX(i);
wait(255);
wait(255);
}
}
}
void setX(unsigned int x){
TRISA=0x00;TRISC=0x00;
PORTA=1; //poids faible
PORTC=x;
PORTA=2;//poids fort
PORTC=x>>8;
}
void setY(unsigned int y){
TRISA=0x00;TRISC=0x00;
PORTA=4; //poids faible
PORTC=x;
PORTA=8;//poids fort
PORTC=x>>8;
}
void wait(unsigned char tempo){
OPTION=0x07; // div 256 et source=quartz
TMR0 =0;
while(TMR0<tempo);
}
Remarquez l’utilisation du timer0 sans interruption pour faire la temporisation.
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 <pic1684.h>
volatile unsigned char PORTC @ 0x07;
volatile unsigned char TRISC @ 0x87;
void setX(unsigned int x);
void setY(unsigned int y);
void wait(unsigned char tempo);
void main(){
int posX=0,posY=0;
signed char deltaX=1,deltaY=1;
while(1){
if ((posX>=620) && (deltaX>0)) deltaX= -deltaX;
if ((posX<=40) && (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);
wait(250);
wait(250);
}
}
void setX(unsigned int x){
TRISA=0x00;TRISC=0x00;
PORTA=1; //poids faible
PORTC=x;
PORTA=2;//poids fort
PORTC=x>>8;
}
void setY(unsigned int y){
TRISA=0x00;TRISC=0x00;
PORTA=4; //poids faible
PORTC=x;
PORTA=8;//poids fort
PORTC=x>>8;
}
void wait(unsigned char tempo){
OPTION=0x07; // div 256 et source=quartz
TMR0 =0;
while(TMR0<tempo);
}
L'inconvénient de ce programme est le petit nombre des trajectoires gérées, ce qui peut devenir lassant pour un jeu.
Ajouter les bords, et les raquettes
[modifier | modifier le wikicode]Comme nous voulons deux raquettes, il nous faut deux coordonnées verticales sur 9 bits (soit 4 ports 8 bits). Nous voulons aussi deux coordonnées de balles soit encore 4 ports de 8 bits. Cela fait en tout 8 ports. Nous avons déjà eu l’occasion de montrer comment on pouvait augmenter le nombre de PORTs.
Solution simple sans bord
[modifier | modifier le wikicode]Nous avons l'intention dans cette section de mettre en œuvre le programme VHDL de la section Ajouter un "strobe" au PORTC et augmenter le nombre de ports. Puisque ce programme VHDL ne proposait que quatre PORTs de sortie nous allons présenter un ensemble fonctionnant mais avec des positions de raquette fixes. Si l’on numérote les quatre ports de sortie de 0 à 3 on choisit
- choix technologiques
E/S module VGA E/S CQPIC x_rect<9:8> PORT1<1:0> x_rect<7:0> PORT0<7:0> y_rect<9:8> PORT3<1:0> y_rect<7:0> PORT2<7:0>
Pour pouvoir gérer des rectangles de tailles différentes et de couleur différentes, on complique un peu la partie destinée à dessiner un rectangle en lui donnant une couleur de rectangle une largeur et une hauteur. Voici donc notre nouveau composant :
COMPONENT rect IS PORT(
row,col,x_rec,y_rec,delta_x,delta_y :in STD_LOGIC_VECTOR(9 DOWNTO 0);
colorRGB : in STD_LOGIC_VECTOR(2 DOWNTO 0);
red1,green1,blue1 : out std_logic);
END component;
L'intanciation des rectangles pour la balle et les raquettes se fera alors de la manière suivante :
balle:rect port map(row=>srow, col=>scol,r ed1=>sred, green1=>sgreen, blue1=>sblue, colorRGB=>"111", delta_x=>"0000001010", delta_y=>"0000001100", x_rec => x_rect, y_rec => y_rect);
raquetteG:rect port map(row=>srow, col=>scol, red1=>sred1, green1=>sgreen1,
blue1=>sblue1, colorRGB=>"100", delta_x=>"0000001010",
delta_y=>"0000111010", x_rec => "0000010110",
y_rec(8 downto 1) => y_raquG, y_rec(9)=>'0',y_rec(0)=>'0');
raquetteD:rect port map(row=>srow, col=>scol, red1=>sred2, green1=>sgreen2,
blue1=>sblue2,colorRGB=>"100", delta_x=>"0000001010",
delta_y=>"0000111010", x_rec => "1001001000",
y_rec(8 downto 1) => y_raquD,y_rec(9)=>'0',y_rec(0)=>'0');
red <= sred or sred1 or sred2;
green <= sgreen or sgreen1 or sgreen2;
blue <= sblue or sblue1 or sblue2;
Les déclarations des signaux dans ce morceau de programme sont omises. Voici maintenant le programme C permettant le rebond sur les raquettes. Vous pouvez remarquer que les coordonnées des raquettes sont calculées en fonction des interrupteurs sur le PORTB, mais ne sont pas mises à jour dans le matériel puisque le matériel ne peut pas le faire.
//#include <pic1684.h> // Programme pour Hitech C dans MPLAB
#include <htc.h> //***** Hitech C *******
volatile unsigned char PORTC @ 0x07;
volatile unsigned char TRISC @ 0x87;
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(RB2==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}} PORTB/raquette
if (RB6) if (raqG_y<215) raqG_y++;
if (RB7) if (raqG_y>0) raqG_y--;
//PORT4=raqG_y;
if (RB0) if (raqD_y<215) raqD_y++;
if (RB1) if (raqD_y>0) raqD_y--;
//PORT5=raqD_y;
wait(250);
wait(250);
}
}
}
Comme on peut le voir nous avons choisi le PORTB connecté aux interrupteurs sw0,sw1,sw6 et sw7 de la carte, pour faire descendre et monter les raquettes et sw2 (RB2) pour le départ.
Travail à réaliser
[modifier | modifier le wikicode]Analyse
[modifier | modifier le wikicode]Comprendre la partie matérielle. Vous devez être capable de dessiner les composants et leurs liaisons à partir des fichiers VHDL donnés dans le répertoire "\CQPICStart\VGA_Pong" du fichier CQPICStart.zip.
Synthèse
[modifier | modifier le wikicode]Étendre la partie matérielle pour gérer le déplacement des deux raquettes. On choisira une coordonnée Y de chacune des raquettes, au choix, sur 8 bits (comme dans le projet silicore1657 de l'année dernière) ou sur 9/10 bits. Pour un choix sur 8 bits on pourra prendre :
- choix technologiques
E/S module VGA E/S CQPIC x_rect<9:8> PORT1<1:0> x_rect<7:0> PORT0<7:0> y_rect<9:8> PORT3<1:0> y_rect<7:0> PORT2<7:0> y_raqG<7:0> PORT4<7:0> y_raqD<7:0> PORT5<7:0>
Le contenu de la gestion des ports est donné dans l'exercice plus haut. Voici le complément :
-- CQPIC.vhd
-- CQPIC top (PIC16F8 with CPU-core, program ROM and data RAM)
-- (1) Version 1.00a Nov 1 1999
--
-- Copyright(c)1999-2002 Sumio Morioka
-- e-mail:morioka@fb3.so-net.ne.jp, URL:http://www02.so-net.ne.jp/~morioka
-- (5) version 1.10a May 13 2010 (Serge Moutou)
-- remove the single bit Input/Outout and replace them with vectors
-- (6) version 1.10b May 15 2010 (Serge Moutou)
-- increase the number of ports using strobe signal
library ieee;
use ieee.std_logic_1164.all;
use ieee.std_logic_arith.all;
entity cqpic is
port (
--> changed ver1.10a, 2010/05/13 (Serge Moutou)
-- ra4_in : in std_logic;
-- ra3_in : in std_logic;
-- ra2_in : in std_logic;
-- ra1_in : in std_logic;
-- ra0_in : in std_logic;
ra_in : in std_logic_vector(7 downto 0);
-- rb7_in : in std_logic;
-- rb6_in : in std_logic;
-- rb5_in : in std_logic;
-- rb4_in : in std_logic;
-- rb3_in : in std_logic;
-- rb2_in : in std_logic;
-- rb1_in : in std_logic;
-- rb0_in : in std_logic;
rb_in : in std_logic_vector(7 downto 0);
--<
ponrst_n : in std_logic;
mclr_n : in std_logic;
clkin : in std_logic;
wdtena : in std_logic;
wdtclk : in std_logic;
--> changed ver1.10a, 2010/05/13 (Serge Moutou)
-- ra4_out : out std_logic;
-- ra3_out : out std_logic;
-- ra2_out : out std_logic;
-- ra1_out : out std_logic;
-- ra0_out : out std_logic;
ra_out : out std_logic_vector(7 downto 0);
-- rb7_out : out std_logic;
-- rb6_out : out std_logic;
-- rb5_out : out std_logic;
-- rb4_out : out std_logic;
-- rb3_out : out std_logic;
-- rb2_out : out std_logic;
-- rb1_out : out std_logic;
-- rb0_out : out std_logic;
rb_out : out std_logic_vector(7 downto 0);
-- ra4_dir : out std_logic;
-- ra3_dir : out std_logic;
-- ra2_dir : out std_logic;
-- ra1_dir : out std_logic;
-- ra0_dir : out std_logic;
ra_dir : out std_logic_vector(7 downto 0);
-- rb7_dir : out std_logic;
-- rb6_dir : out std_logic;
-- rb5_dir : out std_logic;
-- rb4_dir : out std_logic;
-- rb3_dir : out std_logic;
-- rb2_dir : out std_logic;
-- rb1_dir : out std_logic;
-- rb0_dir : out std_logic;
rb_dir : out std_logic_vector(7 downto 0);
--<
--> added ver1.10b May 22 2010 (Serge Moutou)
rc_out, rc_dir : out std_logic_vector(7 downto 0); -- PORT-C data
rc_in : in std_logic_vector(7 downto 0);
rd_out : out std_logic_vector(7 downto 0); -- PORT-D data
re_out : out std_logic_vector(7 downto 0); -- PORT-E data
rf_out : out std_logic_vector(7 downto 0); -- PORT-F data
--<
clkout : out std_logic;
wdtfull : out std_logic;
powerdown : out std_logic;
startclkin : out std_logic
);
end cqpic;
architecture RTL of cqpic is
-- CPU core
component piccore
generic (
STACK_SIZE : integer;
WDT_SIZE : integer
);
port (
progdata : in std_logic_vector(13 downto 0);
progadr : out std_logic_vector(12 downto 0);
ramdtin : in std_logic_vector(7 downto 0);
ramdtout : out std_logic_vector(7 downto 0);
ramadr : out std_logic_vector(8 downto 0);
readram : out std_logic;
writeram : out std_logic;
existeeprom : in std_logic;
eepdtin : in std_logic_vector(7 downto 0);
eepdtout : out std_logic_vector(7 downto 0);
eepadr : out std_logic_vector(7 downto 0);
readeepreq : out std_logic;
readeepack : in std_logic;
writeeepreq : out std_logic;
writeeepack : in std_logic;
--> changed ver1.10a, 2010/05/13 (Serge moutou)
-- porta_in : in std_logic_vector(4 downto 0);
-- porta_out : out std_logic_vector(4 downto 0);
-- porta_dir : out std_logic_vector(4 downto 0);
porta_in : in std_logic_vector(7 downto 0);
porta_out : out std_logic_vector(7 downto 0);
porta_dir : out std_logic_vector(7 downto 0);
--<
portb_in : in std_logic_vector(7 downto 0);
portb_out : out std_logic_vector(7 downto 0);
portb_dir : out std_logic_vector(7 downto 0);
rbpu : out std_logic;
--> added ver1.10b May 22 2010 (Serge Moutou)
portc_in : in std_logic_vector(7 downto 0); -- PORT-C input data
portc_out : out std_logic_vector(7 downto 0); -- PORT-C output data
portc_dir : out std_logic_vector(7 downto 0); -- TRISC: PORT-C signal direction (H:input, L:output)
portc_strobe: out std_logic; -- PORT_C strobe
--<
int0 : in std_logic;
int4 : in std_logic;
int5 : in std_logic;
int6 : in std_logic;
int7 : in std_logic;
t0cki : in std_logic;
wdtena : in std_logic;
wdtclk : in std_logic;
wdtfull : out std_logic;
powerdown : out std_logic;
startclkin : out std_logic;
ponrst_n : in std_logic;
mclr_n : in std_logic;
clkin : in std_logic;
clkout : out std_logic
);
end component;
-- program ROM
component progrom
port (
romaddr : in std_logic_vector(12 downto 0);
clk : in std_logic;
romout : out std_logic_vector(13 downto 0)
);
end component;
-- data RAM (SRAM)
component dataram
port (
addr : in std_logic_vector(8 downto 0);
read : in std_logic;
write : in std_logic;
clk : in std_logic;
datain : in std_logic_vector(7 downto 0);
dataout : out std_logic_vector(7 downto 0)
);
end component;
--> added ver1.10b May 22 2010 (Serge Moutou)
component ports
port(
clk : in std_logic;
strobeC_in : in std_logic;
portA : in std_logic_vector(7 downto 0);
portC : in std_logic_vector(7 downto 0);
pA,pB,pC,pD,pE,pF : out std_logic_vector(7 downto 0) --neww ports
);
end component;
--<
signal romaddr : std_logic_vector(12 downto 0);
signal romdata : std_logic_vector(13 downto 0);
signal ramaddr : std_logic_vector(8 downto 0);
signal ramindata : std_logic_vector(7 downto 0);
signal ramoutdata : std_logic_vector(7 downto 0);
signal readram : std_logic;
signal writeram : std_logic;
--> changed ver1.10a, 2010/05/13 (Serge Moutou)
-- signal rain_node : std_logic_vector(4 downto 0);
-- signal raout_node : std_logic_vector(4 downto 0);
-- signal radir_node : std_logic_vector(4 downto 0);
signal rain_node : std_logic_vector(7 downto 0);
signal raout_node : std_logic_vector(7 downto 0);
signal radir_node : std_logic_vector(7 downto 0);
--<
signal rbin_node : std_logic_vector(7 downto 0);
signal rbout_node : std_logic_vector(7 downto 0);
signal rbdir_node : std_logic_vector(7 downto 0);
--> added ver1.10b May 22 2010 (Serge Moutou)
signal rcin_node : std_logic_vector(7 downto 0);
signal rcout_node : std_logic_vector(7 downto 0);
signal rcdir_node : std_logic_vector(7 downto 0);
signal s_strobe : std_logic;
--<
signal VCC_node : std_logic;
signal GND_node : std_logic;
signal GND_vec_node : std_logic_vector(7 downto 0);
begin
VCC_node <= '1';
GND_node <= '0';
GND_vec_node <= "00000000";
U1: piccore
generic map (
STACK_SIZE => 8,
WDT_SIZE => 255
)
port map (
progdata => romdata, -- in
progadr => romaddr, -- out
ramdtin => ramoutdata, -- in
ramdtout => ramindata, -- out
ramadr => ramaddr, -- out
readram => readram, -- out
writeram => writeram, -- out
existeeprom => GND_node, -- in
eepdtin => GND_vec_node, -- in
-- NC eepdtout => , -- out
-- NC eepadr => , -- out
-- NC readeepreq => , -- out
readeepack => VCC_node, -- in
-- NC writeeepreq => , -- out
writeeepack => VCC_node, -- in
porta_in => rain_node, -- in
porta_out => raout_node, -- out
porta_dir => radir_node, -- out
portb_in => rbin_node, -- in
portb_out => rbout_node, -- out
portb_dir => rbdir_node, -- out
--> added ver1.10b May 22 2010 (Serge Moutou)
portc_in => rcin_node, -- in
portc_out => rcout_node, -- out
portc_dir => rcdir_node, -- out
portc_strobe => s_strobe,
--<
-- NC rbpu => -- out
int0 => rbin_node(0), -- in
int4 => rbin_node(4), -- in
int5 => rbin_node(5), -- in
int6 => rbin_node(6), -- in
int7 => rbin_node(7), -- in
t0cki => rain_node(4), -- in
wdtena => wdtena, -- in
wdtclk => wdtclk, -- in
wdtfull => wdtfull, -- out
powerdown => powerdown, -- out
startclkin => startclkin, -- out
-- added "not" (Serge Moutou 2010/05) for Digilent board
ponrst_n => not ponrst_n, -- in
mclr_n => not mclr_n, -- in
clkin => clkin, -- in
clkout => clkout -- out
);
U2: progrom
port map (
romaddr => romaddr,
clk => clkin,
romout => romdata
);
U3: dataram
port map (
addr => ramaddr,
read => readram,
write => writeram,
clk => clkin,
datain => ramindata,
dataout => ramoutdata
);
--> added ver1.10b May 22 2010 (Serge Moutou)
U4: ports
port map (
clk => clkin,--rbout_node(0),
strobeC_in => s_strobe,--rbout_node(1),
portA => raout_node,
portC => rcout_node,
pA => ra_out, --neww ports
pB => rb_out,
pC => rc_out,--pC => rd_out,
pD => rd_out,--pD => re_out,
pE => re_out,
pF => rf_out
);
--<
--> changed ver1.10a, 2010/05/13 (Serge Moutou)
-- rain_node(4) <= ra4_in;
-- rain_node(3) <= ra3_in;
-- rain_node(2) <= ra2_in;
-- rain_node(1) <= ra1_in;
-- rain_node(0) <= ra0_in;
rain_node <= ra_in;
-- ra4_out <= raout_node(4);
-- ra3_out <= raout_node(3);
-- ra2_out <= raout_node(2);
-- ra1_out <= raout_node(1);
-- ra0_out <= raout_node(0);
--removed ver1.10b, 2010/05/15 ra_out <= raout_node;
-- ra4_dir <= radir_node(4);
-- ra3_dir <= radir_node(3);
-- ra2_dir <= radir_node(2);
-- ra1_dir <= radir_node(1);
-- ra0_dir <= radir_node(0);
ra_dir <= radir_node;
-- rbin_node(7) <= rb7_in;
-- rbin_node(6) <= rb6_in;
-- rbin_node(5) <= rb5_in;
-- rbin_node(4) <= rb4_in;
-- rbin_node(3) <= rb3_in;
-- rbin_node(2) <= rb2_in;
-- rbin_node(1) <= rb1_in;
-- rbin_node(0) <= rb0_in;
rbin_node <= rb_in;
-- rb7_out <= rbout_node(7);
-- rb6_out <= rbout_node(6);
-- rb5_out <= rbout_node(5);
-- rb4_out <= rbout_node(4);
-- rb3_out <= rbout_node(3);
-- rb2_out <= rbout_node(2);
-- rb1_out <= rbout_node(1);
-- rb0_out <= rbout_node(0);
-- removed ver1.10b, 2010/05/15 rb_out <= rbout_node;
-- rb7_dir <= rbdir_node(7);
-- rb6_dir <= rbdir_node(6);
-- rb5_dir <= rbdir_node(5);
-- rb4_dir <= rbdir_node(4);
-- rb3_dir <= rbdir_node(3);
-- rb2_dir <= rbdir_node(2);
-- rb1_dir <= rbdir_node(1);
-- rb0_dir <= rbdir_node(0);
rb_dir <= rbdir_node;
--<
--> added ver1.10b May 22 2010 (Serge Moutou)
rcin_node <= rc_in;
rc_dir <= rcdir_node;
--rc_out <= rcout_node; -- c<=b:normal
-- rb_out <= rbout_node;
-- ra_out <= raout_node;
--<
end RTL;
Programmation
[modifier | modifier le wikicode]Explorer s'il n’est pas possible d’utiliser l'algorithme de tracé de segment de droite Bresenham pour les trajectoires de balles. Cet algorithme est expliqué dans le WIKI : Algorithme de tracé de segment de Bresenham. Il est possible de trouver directement une version en C : tapez Bresenham en C dans google.
Premièrement : modifier le programme pour utiliser setXY au lieu et place de setX et setY :
//#include <pic1684.h> // Programme pour Hitech C dans MPLAB
#include <htc.h>
volatile unsigned char PORTC @ 0x07;
volatile unsigned char TRISC @ 0x87;
void setXY(unsigned int x,unsigned int y);
void wait(unsigned char tempo);
unsigned int posRaqu_16;
void main(){
unsigned int posX,posY;
unsigned char raqD_y=0,raqG_y=0;
signed char deltaX=1,deltaY=1;
TRISB=0xFF;// entree
while(1){
posX=113;
posY=101;
setXY(posX,posY);
while(RB2==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;
if ((posY>=460) && (deltaY>0)) deltaY= -deltaY;
if ((posY<=10) && (deltaY<0)) deltaY= -deltaY;
posY=posY+deltaY;
setXY(posX,posY);
// gestion des raquettes {{Unité|2|bits}} PORTB/raquette
if (RB6) if (raqG_y<215) raqG_y++;
if (RB7) if (raqG_y>0) raqG_y--;
//PORTE=raqG_y;
PORTA=16;
PORTC=raqG_y;
if (RB0) if (raqD_y<215) raqD_y++;
if (RB1) if (raqD_y>0) raqD_y--;
//PORTF=raqD_y;
PORTA=32;
PORTC=raqD_y;
wait(250);
wait(250);
}
}
}
void setXY(unsigned int x,unsigned int y){
TRISA=0x00;TRISC=0x00;
PORTA=1; //poids faible
PORTC=x;
PORTA=2;//poids fort
PORTC=x>>8;
PORTA=4; //poids faible
PORTC=y;
PORTA=8;//poids fort
PORTC=y>>8;
}
void wait(unsigned char tempo){
OPTION=0x07; // div 256 et source=quartz
TMR0 =0;
while(TMR0<tempo);
}
Pour l’algorithme de tracé de segment de Bresenham, le programme le plus compact trouvé sur internet est :
//********* http://rosettacode.org/wiki/Bitmap/Bresenham's_line_algorithm
void line(int x0, int y0, int x1, int y1) {
int dx = abs(x1-x0), sx = x0<x1 ? 1 : -1;
int dy = abs(y1-y0), sy = y0<y1 ? 1 : -1;
int err = (dx>dy ? dx : -dy)/2, e2;
for(;;){
setPixel(x0,y0);
if (x0==x1 && y0==y1) break;
e2 = err;
if (e2 >-dx) { err -= dy; x0 += sx; }
if (e2 < dy) { err += dx; y0 += sy; }
}
}
La première chose que fait l'algorithme est de calculer dx et dy. En ce qui nous concerne, nous ne ferons pas ce calcul puisque nous gérerons directement ce dx et ce dy. Entre parenthèse, cela nous évite de calculer des points d'intersections de notre trajectoire avec les bords de l'écran.
En remarquant que x0 est notre posX, y0 est notre posY, sx est notre deltaX et donc sy notre delatY, il vient :
#include <pic1684.h> // Programme pour Hitech C dans MPLAB
volatile unsigned char PORTC @ 0x07;
volatile unsigned char TRISC @ 0x87;
void setXY(unsigned int x,unsigned int y);
void wait(unsigned char tempo);
unsigned int posRaqu_16;
void main(){
unsigned int posX,posY;
int dx=10,dy=10,err=-5,e2; //Pour Bresenham
unsigned char raqD_y=0,raqG_y=0;
signed char deltaX=1,deltaY=1;
TRISB=0xFF;// entree
while(1){
posX=113;
posY=101;
setXY(posX,posY);
while(RB2==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; Retiré pour Bresenham
if ((posY>=460) && (deltaY>0)) deltaY= -deltaY;
if ((posY<=10) && (deltaY<0)) deltaY= -deltaY;
//*********** Début de Brensenham
e2 = err;
if (e2 >-dx) { err -= dy; posX += deltaX; }
if (e2 < dy) { err += dx; posY += deltaY; }
// posY=posY+deltaY;
setXY(posX,posY);
//*********** fin de Bresenham
// gestion des raquettes {{Unité|2|bits}} PORTB/raquette
if (RB6) if (raqG_y<215) raqG_y++;
if (RB7) if (raqG_y>0) raqG_y--;
//PORTE=raqG_y;
PORTA=16;
PORTC=raqG_y;
if (RB0) if (raqD_y<215) raqD_y++;
if (RB1) if (raqD_y>0) raqD_y--;
//PORTF=raqD_y;
PORTA=32;
PORTC=raqD_y;
wait(250);
wait(250);
}
}
}
//************* voir ci-dessus pour setXY et wait
Il ne reste plus qu’à faire varier deltaY entre 15 et 5 par exemple à l'aide des rebonds sur les raquettes.
Certains se demandent peut-être ce qu’il y a de Bresenham là-dedans. Je vous rappelle que l'algorithme original permet de calculer les points d'une droite sans multiplication et sans division, c’est ce que l’on fait ici. Ce qui caractérise cet algorithme c’est que la pente de la droite tracée est dy/dx, c'est-à-dire un nombre rationnel, c’est ce que l’on a encore une fois. Changez la ligne
// int err = (dx>dy ? dx : -dy)/2, e2; montre comment calculer err
int dx=10,dy=10,err=-5,e2; //Pour Bresenham
en
// int err = (dx>dy ? dx : -dy)/2, e2; montre comment calculer err
int dx=10,dy=5,err=+5,e2; //Pour Bresenham
ou encore en
// int err = (dx>dy ? dx : -dy)/2, e2; montre comment calculer err
int dx=10,dy=15,err=-7,e2; //Pour Bresenham
pour voir les différentes pentes du déplacement de la balle.
Ne pas oublier que dès que dy ou dx varie il faut mettre à jour la variable err pour bien faire ! Seule cette opération nécessite une division par 2.
Voici un exemple qui change la pente de la balle en fonction du mouvement de la raquette au moment du rebond. Pour bien faire, il faudrait que si le déplacement de la raquette et de la balle sont dans le même sens la pente augmente, s'ils sont dans la direction opposée la pente diminue et si les raquettes sont immobiles la pente reste constante. Cela revient à gérer les rebonds sur les raquettes, par exemple, de la manière suivante :
- dx est fixé à 10 et ne change jamais
- si les raquettes sont immobiles au moment du rebond dy ne change pas
- si le déplacement de la balle (suivant y) et de la raquette ont même signe on augmente dy en ne dépassant jamais 15
- si le déplacement de la raquette et de la balle (suivant y) sont en sens contraires on diminue dy en ne dépassant jamais 5
#include <pic1684.h> // Programme pour Hitech C dans MPLAB
volatile unsigned char PORTC @ 0x07;
volatile unsigned char TRISC @ 0x87;
void setXY(unsigned int x,unsigned int y);
void wait(unsigned char tempo);
unsigned int posRaqu_16;
void main(){
unsigned int posX,posY;
// int err = (dx>dy ? dx : -dy)/2, e2;
int dx=10,dy=10,err=-5,e2; //Brensenham
unsigned char raqD_y=0,raqG_y=0;
signed char deltaX=1,deltaY=1,delta_raqG_y,delta_raqD_y;
TRISB=0xFF;// entree
while(1){
posX=113;
posY=101;
setXY(posX,posY);
while(RB2==0); // attente départ
dx=10;dy=10;err=-5; //reinitialisation Bresenham
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;
if (delta_raqD_y) { // delta_raqG_y!=0 on change la pente
if ((delta_raqD_y >0 && deltaY >0) ||(delta_raqD_y <0 && deltaY <0)) {
dy+=1; if (dy > 15) dy=15; // ne peut dépasser 15
// int err = (dx>dy ? dx : -dy)/2, e2;
if (dx>dy) err = dx/2; else err = -dy/2;
} else {
dy-=1; if (dy < 5) dy=5; // ne peut dépasser 5
// int err = (dx>dy ? dx : -dy)/2, e2;
if (dx>dy) err = dx/2; else err = -dy/2;
}
}
}
posRaqu_16=raqG_y<<1;
if ((posX<=32) && (posY<posRaqu_16+58) && (posY+10>posRaqu_16) && (deltaX<0)) {
deltaX= -deltaX;
if (delta_raqG_y) { // delta_raqG_y!=0 on change la pente
if ((delta_raqG_y >0 && deltaY >0) ||(delta_raqG_y <0 && deltaY <0)) {
dy+=1; if (dy > 15) dy=15; // ne peut dépasser 15
// int err = (dx>dy ? dx : -dy)/2, e2;
if (dx>dy) err = dx/2; else err = -dy/2;
} else {
dy-=1; if (dy < 5) dy=5; // ne peut dépasser 5
// int err = (dx>dy ? dx : -dy)/2, e2;
if (dx>dy) err = dx/2; else err = -dy/2;
}
}
}
// posX=posX+deltaX; Removed
if ((posY>=460) && (deltaY>0)) deltaY= -deltaY;
if ((posY<=10) && (deltaY<0)) deltaY= -deltaY;
// gestion des raquettes {{Unité|2|bits}} PORTB/raquette
delta_raqG_y=0;
if (RB6) if (raqG_y<215) delta_raqG_y=1;
if (RB7) if (raqG_y>0) delta_raqG_y=-1;
raqG_y+=delta_raqG_y;
//PORTE=raqG_y;
PORTA=16;
PORTC=raqG_y;
delta_raqD_y=0;
if (RB0) if (raqD_y<215) delta_raqD_y=1;
if (RB1) if (raqD_y>0) delta_raqD_y=-1;
raqD_y+=delta_raqD_y;;
//PORTF=raqD_y;
PORTA=32;
PORTC=raqD_y;
// Brensenham
e2 = err;
if (e2 >-dx) { err -= dy; posX += deltaX; }
if (e2 < dy) { err += dx; posY += deltaY; }
// posY=posY+deltaY;
setXY(posX,posY);
wait(250);
wait(250);
}
}
}
Pour information l’ensemble des resources mémoire de ce programme est donné maintenant :
Memory Summary: Program space used 396h ( 918) of 400h words ( 89.6%) Data space used 21h ( 33) of A4h bytes ( 20.1%) EEPROM space used 0h ( 0) of 40h bytes ( 0.0%) Configuration bits used 0h ( 0) of 1h word ( 0.0%) ID Location space used 0h ( 0) of 4h bytes ( 0.0%)
Ce qui montre qu’il ne nous reste plus grand chose au niveau de la mémoire de code !!! Bon une bonne nouvelle quand même :
- Dans l'environnement MPLAB, aller dans : Project → Build Options → project →onglet "global" et remplir "ROM" avec : default,+400-7FF.
Ceci double la ROM disponible dans notre cœur... et toute cette mémoire est disponible sans problème dans notre FPGA.
Synthèse matérielle et programmation
[modifier | modifier le wikicode]Gérer un affichage des scores dans la partie basse de l'écran, soit avec une mémoire de caractères, soit avec des afficheurs sept segments à construire.
Voila ce que cela donne avec un affichage sept segments simulé.
Annexe 1
[modifier | modifier le wikicode]Le programme C++ pour transformer le fichier HEX en fichier VHDL n’est pas donné car un petit peu trop long. Il se trouve dans le répertoire "\CQPICStart\ROM" du fichier CQPICStart.zip
Ce n’est pas le fichier original hex2vhd.c proposé avec le cœur CQPIC qui est utilisé. C'est le fichier hex2rom.cpp présent dans un cœur concurrent, à savoir le cœur PPX. Ce fichier a été légèrement modifié pour que le programme généré puisse aller directement dans notre projet VHDL. |
Un fois compilé mon utilitaire s’appelle hex2rom.exe (sous windows) ou hex2rom sous Linux. L'utilisation est faite par la ligne de commande :
hex2rom demo16F84.hex progrom 13l14s >progrom.vhd
si le fichier à convertir s’appelle demo16F84.hex.
Le fichier généré s’appelle progrom.vhd et a "progrom" comme nom d'entité. 13 est le nombre de bit d'adresses, l désigne le fait que l’on utilise la convention little indian (b désignerait big indian) et 14 est le nombre de bits de données dans la ROM.
Si l’on utilise le fichier VHDL directement sans modifier les options de synthèse, nous nous trouvons avec de la ROM distribuée. C'est à vous de décider si c’est grave ou pas.
L'ensemble des blocs utilisés se trouve en blanc et cet exemple montre bien pourquoi on appelle cela de la RAM distribuée (points blancs répartis dans le FPGA). Les blocs RAM (rectangles rouges) sont donc inutilisés (en fait ils sont partiellement utilisés par la RAM du 16F84). Vous pourrez noter que cette façon de faire est très consommatrice de ressources.
La différence entre RAM distribuée et bloc RAM (BRAM) est expliquée dans un autre projet.
Un essai avec une ROM minimale me montre une synthèse qui prend seulement 5% du Spartan3 200 contre 80% avec un programme conséquent. Ces chiffres nous ont conduit à considérer sérieusement la possibilité d’utiliser des BRAM dans ce projet. Ce travail bien fait pourrait nous autoriser à utiliser data2mem comme expliqué dans un autre chapitre donc gagner énormément en taille et en souplesse. Consulter la section Utiliser data2mem avec CQPIC pour voir l'état d'avancement de ce futur travail.
Annexe II : le fichier ucf
[modifier | modifier le wikicode]Le fichier ucf dépend à la fois du FPGA utilisé et de la carte de développement.
#PORTB sur leds net "rb0_out" loc="K12"; net "rb1_out" loc="P14"; net "rb2_out" loc="L12"; net "rb3_out" loc="N14"; net "rb4_out" loc="P13"; net "rb5_out" loc="N12"; net "rb6_out" loc="P12"; net "rb7_out" loc="P11"; #PORTB sur interrupteurs net "rb_in<7>" loc="k13"; net "rb_in<6>" loc="k14"; net "rb_in<5>" loc="j13"; net "rb_in<4>" loc="j14"; net "rb_in<3>" loc="h13"; net "rb_in<2>" loc="h14"; net "rb_in<1>" loc="g12"; net "rb_in<0>" loc="f12"; # clock net "clkin" loc="T9"; net "clkin" TNM_NET = "clkin"; TIMESPEC "TS_mclk" = PERIOD "clkin" 20 ns HIGH 50 %; # reset net "mclr_n" loc="M13"; net "ponrst_n" loc="M14"; #VGA net "hsynch" loc="R9"; net "vsynch" loc="T10"; net "red" loc="R12"; net "blue" loc="R11"; net "green" loc="T12";
Ce fichier ucf fonctionne correctement avec la version 9.2 de l'IDE mais la version 11.1 nécessite le rajout de :
NET "rb_in<0>" CLOCK_DEDICATED_ROUTE = FALSE; NET "rb_in<4>" CLOCK_DEDICATED_ROUTE = FALSE; NET "rb_in<5>" CLOCK_DEDICATED_ROUTE = FALSE; NET "rb_in<6>" CLOCK_DEDICATED_ROUTE = FALSE; NET "rb_in<7>" CLOCK_DEDICATED_ROUTE = FALSE;
en tout début de fichier.
Voir aussi
[modifier | modifier le wikicode]- System on Chip
- Processeur softcore
- La version pdf, une version parfois plus complète de ce document.
- PIC 16F84 de Microchip
- Conception et VHDL
- Comment démarrer avec un PIC 16F84
- Utiliser les PIC 16F et 18F
- Le langage C chez Wikipédia
- La programmation C et ses exercices chez WikiBook
- Introduction au langage C chez Wikiversité
- la carte Spartan-3 Starter Board
- ISE Xilinx pour développer en VHDL ou plus exactement le WebPack gratuit. Attention choisir une version qui propose le webPack (12.3 ou 12.2 mais pas 12.1 ou une plus ancienne avec laquelle je travaille 11.1).
- RAM et FPGA dans un FPGA.
- le site du CQPIC