Very High Speed Integrated Circuit Hardware Description Language/Micro contrôleur embarqué : le PicoBlaze

Leçons de niveau 15
Une page de Wikiversité, la communauté pédagogique libre.
Début de la boite de navigation du chapitre
Micro contrôleur embarqué : le PicoBlaze
Icône de la faculté
Chapitre no 7
Leçon : Very High Speed Integrated Circuit Hardware Description Language
Chap. préc. :Petit Système monopuce MCPU
Chap. suiv. :Système monopuce compatible avec les PIC 16C57
fin de la boite de navigation du chapitre
En raison de limitations techniques, la typographie souhaitable du titre, « Very High Speed Integrated Circuit Hardware Description Language : Micro contrôleur embarqué : le PicoBlaze
Very High Speed Integrated Circuit Hardware Description Language/Micro contrôleur embarqué : le PicoBlaze
 », n'a pu être restituée correctement ci-dessus.

Le picoblaze est probablement l'architecture par laquelle on doit commencer pour apprendre à utiliser un processeur softcore dans un FPGA Xilinx. Il a une architecture très simple qui peut facilement s'étendre matériellement.

Le modèle de programmation du PicoBlaze[modifier | modifier le wikicode]

Le modèle de programmation est très simple. Le picoBlaze est constitué de 16 registres 8 bits d'utilisation générale notés de s0 à sF. On trouve aussi un compteur programme sur 10 bits, un pointeur de pile (tos = Top Of Stack) sur 31 niveaux et un registre d'état avec 3 bits appelés drapeaux :

  • C : retenue (Carry)
  • Z : zéro
  • I : masque d'interruption

Les deux mémoires, programme et données, sont organisées de la manière suivante:

  • La mémoire programme est organisée en 1024 instructions sur 18 bits. L'adressage de la mémoire programme se fait donc sur 10 bits ().
  • La mémoire donnée est organisée en 64 octets. L'adressage se fait donc sur 6 bits ()


Quelques instructions[modifier | modifier le wikicode]

Les modes d'adressage[modifier | modifier le wikicode]

Le PicoBlaze connaît cinq modes d'adressage.

  • Adressage registre : c’est l'adressage le plus simple, il ne peut concerner que des registres.
Instruction en adressage registre
Op-code (Hexa) instruction opérande(s) commentaire
0 10 10 load s0,s1 ;charger s1 dans s0


  • Adressage immédiat (Immediate) : l’opérande correspondant se trouve directement dans le programme derrière le code de l’instruction et un premier opérande. Il se fait sur 8 bits.
0 00 7F 	load s0,7F 	;charger 127 dans s0
1 80 10 	add s0,10	;additionner 16


Instruction en adressage immédiat
instruction opérande 1 opérande 2 Op-code
LOAD sX aaaa aaaa 00 0000 xxxx aaaa aaaa
ADD sX aaaa aaaa 01 0010 xxxx aaaa aaaa

sX désigne un registre dont le numéro X peut varier de 0 à 15 qui est donc codé sur 4 bits "xxxx".

  • Immédiat 10 bits : l'adresse est donnée sur 10 bits. L'instruction JUMP utilise ce mode d'adressage parce que l'adresse fournie est une adresse absolue de programme.
Instruction en adressage immédiat 10 bits
instruction opérande 1 opérande 2 Op-code
JUMP aa aaaa aaaa 11 0100 00aa aaaa aaaa
  • Direct 6 bits : l'adresse de la mémoire se trouve dans le dernier octet du code de l'instruction (sur les 6 bits de poids faible). Les instructions STORE et FETCH fonctionnent avec ce mode d'adressage et permettent d'adresser 64 octets (seulement) de la mémoire.
0601F		FETCH s0,1F	;charge le contenu de 1F dans s0
18056		ADD s0,56	;additionne 56 en hexadécimal
34188		JUMP 188	;saute à l'adresse 188 
				;(sur {{Unité|10|bits}})

À noter encore les opcodes sur 18 bits (5 caractères hexadécimaux).

Instruction en adressage direct 6 bits (RAM)
instruction opérande 1 opérande 2 Op-code
FETCH sX aa aaaa 00 0110 xxxx 00aa aaaa

L'instruction FETCH est une instruction qui permet de lire une donnée en mémoire.

  • Adressage indexé : l'adresse de la mémoire se trouve dans un registre. Nous donnons ci-dessous le format de cette instruction.
Instruction en adressage indexé par un registre (RAM)
instruction opérande 1 opérande 2 Op-code
FETCH sX (sY) 00 0111 xxxx yyyy 0000

Prenez ce qu’il y a dans le registre sY, cela fait une adresse mémoire que l’on va lire et mettre dans le registre sX. L'intérêt est qu'après cette lecture on peut incrémenter sY et refaire un "FETCH" qui lira l'adresse suivante... ce qui est bien pratique dans une boucle.

Jeu d'instructions[modifier | modifier le wikicode]

Le jeu d'instructions est présenté maintenant sans explications. Si quelqu’un veut ajouter une colonne aux tableaux ci-dessous pour expliquer ce que font les instructions ....

Les instructions de contrôle du picoblaze
instruction opérande 1 opérande 2 Op-code
JUMP aa aaaa aaaa 11 0100 00aa aaaa aaaa
JUMP Z, aa aaaa aaaa 11 0101 00aa aaaa aaaa
JUMP NZ, aa aaaa aaaa 11 0101 01aa aaaa aaaa
JUMP C, aa aaaa aaaa 11 0101 10aa aaaa aaaa
JUMP NC, aa aaaa aaaa 11 0101 11aa aaaa aaaa
CALL aa aaaa aaaa 11 0000 00aa aaaa aaaa
CALL Z, aa aaaa aaaa 11 0001 00aa aaaa aaaa
CALL NZ, aa aaaa aaaa 11 0001 01aa aaaa aaaa
CALL C, aa aaaa aaaa 11 0001 10aa aaaa aaaa
CALL NC, aa aaaa aaaa 11 0001 11aa aaaa aaaa
RETURN 10 1010 0000 0000 0000
RETURN Z, 10 1011 0000 0000 0000
RETURN NZ, 10 1011 0100 0000 0000
RETURN C, 10 1011 1000 0000 0000
RETURN NC, 10 1011 1100 0000 0000
Les instructions de mouvement de données
instruction opérande 1 opérande 2 Op-code
LOAD sX aaaa aaaa 00 0000 xxxx aaaa aaaa
LOAD sX sY 00 0001 xxxx yyyy 0000
FETCH sX aa aaaa 00 0110 xxxx 00aa aaaa
FETCH sX (sY) 00 0111 xxxx yyyy 0000
STORE sX aa aaaa 10 1110 xxxx 00aa aaaa
STORE sX (sY) 10 1111 xxxx yyyy 0000
INPUT sX aaaa aaaa 00 0100 xxxx aaaa aaaa
INPUT sX (sY) 00 0101 xxxx yyyy 0000
OUTPUT sX aaaa aaaa 10 0100 xxxx aaaa aaaa
OUTPUT sX (sY) 10 0101 xxxx yyyy 0000
Instructions arithmétiques
instruction opérande 1 opérande 2 Op-code
ADD sX aaaa aaaa 01 0010 xxxx aaaa aaaa
ADD sX sY 01 0011 xxxx yyyy 0000
ADDCY sX aaaa aaaa 01 1010 xxxx aaaa aaaa
ADDCY sX sY 01 1011 xxxx yyyy 0000
SUB sX aaaa aaaa 01 1100 xxxx aaaa aaaa
SUB sX sY 01 1101 xxxx yyyy 0000
SUBCY sX aaaa aaaa 01 1110 xxxx aaaa aaaa
SUBCY sX sY 01 1111 xxxx yyyy 0000
COMPARE sX aaaa aaaa 01 0100 xxxx aaaa aaaa
COMPARE sX sY 01 0101 xxxx yyyy 0000
Instructions logiques
instruction opérande 1 opérande 2 Op-code
AND sX aaaa aaaa 00 1010 xxxx aaaa aaaa
AND sX (sY) 00 1011 xxxx yyyy 0000
OR sX aaaa aaaa 00 1100 xxxx aaaa aaaa
OR sX (sY) 00 1101 xxxx yyyy 0000
XOR sX aaaa aaaa 00 1110 xxxx aaaa aaaa
XOR sX (sY) 00 1111 xxxx yyyy 0000
TEST sX aaaa aaaa 01 0010 xxxx aaaa aaaa
TEST sX (sY) 01 0011 xxxx yyyy 0000
Instructions de décalage et rotations
instruction opérande 1 opérande 2 Op-code
SR0 sX 10 0000 xxxx 0000 1110
SR1 sX 10 0000 xxxx 0000 1111
SRX sX 10 0000 xxxx 0000 1010
SRA sX 10 0000 xxxx 0000 1000
RR sX 10 0000 xxxx 0000 1100
SL0 sX 10 0000 xxxx 0000 0110
SL1 sX 10 0000 xxxx 0000 0111
SLX sX 10 0000 xxxx 0000 0010
SLA sX 10 0000 xxxx 0000 0000
RL sX 10 0000 xxxx 0000 0100

Une figure nous expliquera un peu ces instructions :

et vous en déduirez facilement ce qui se passe pour les décalages gauches.

Instructions d'interruptions
instruction opérande 1 opérande 2 Op-code
ENABLE INTERRUPT 11 1100 0000 0000 0001
DISABLE INTERRUPT 11 1100 0000 0000 0000
RETURNI ENABLE 11 1000 0000 0000 0001
RETURNI DISABLE 11 1000 0000 0000 0000

Comparaison des mnémoniques et directives[modifier | modifier le wikicode]

Si vous développez autour du PicoBlaze vous serez amené à utiliser plusieurs assembleurs très proches mais pas complètement similaires, celui du KCPSM3 et celui du simulateur PBlazeIDE. À noter que si vous travaillez sous Linux, vous n'aurez pas ce problème. En effet l'unique environnement kpicosim permet de simuler d’une part et de générer la ROM d’autre part.

Nous présentons donc maintenant les différents mnémoniques des deux environnements Windows. KCPSM3 est l'assembleur fourni par Xilinx pour générer les ROMs à partir du programme tandis que PBlazeIDE est un environnement de simulation que l’on peut télécharger gratuitement.

Les différents mnémoniques du picoblaze
KCPSM3 PBlazeIDE
addcy addc
subcy subc
compare comp
store sX, (sY) store sX, sY
fetch sX, (sY) fetch sX, sY
input sX, (sY) in sX, sY
input Sx,KK in sX,$KK
ouput sX, (sY) out sX, sY
return ret
returni reti
enable interrupt eint
disable interrupt dint

Il existe d'autres différences avec les directives que nous présentons maintenant.

Les différents directives du picoblaze
Fonction KCPSM3 PBlazeIDE
positionnement de code address 3FF org $3FF
constante constant MAX, 3F MAX equ $3F
alias de registre namereg addr, s2 addr equ s2
alias de ports constant in_port, 00 in_port dsin $00
constant out_port 10 out_port dsout $10
constant bi_port, 0F bi_port dsio $0F

Mon premier programme en assembleur[modifier | modifier le wikicode]

Nous avons déjà eu l’occasion de présenter quelques champs à la section 3-1. Nous allons présenter d'autres possibilités maintenant. Regardez le programme ci-dessous :

CONSTANT m,10 ;commentaire
ADDRESS 300
	LOAD s3,m
	JUMP suite
suite:	LOAD ...

On distingue dans ce programme une étiquette , une définition symbolique, un commentaire et la définition de l'origine du programme. L'étiquette est un nom suivi de deux points, ici "suite:". La définition symbolique est m pour la valeur 10 (en hexadécimal). L'origine du programme est en adresse 300 (hexadécimal) ; on utilise la directive ADDRESS.

Les structures de contrôle[modifier | modifier le wikicode]

On présente maintenant les structures de contrôle pour le PicoBlaze. Les structures de contrôle utilisent l'instruction COMPARE qu’il faut bien connaître. On la rencontre sous deux formes :

COMPARE sX, sY; Compare sX et sY.
COMPARE sX, kk; Compare sX et la constante kk.

Cette instruction est mieux comprise avec son pseudocode qui montre comment les deux drapeaux Carry et Zero sont affectés par cette instruction :

   if ( Operand > sX ) then
       CARRY    1
   else
       CARRY    0
   endif
   if ( sX = Operand ) then
       ZERO   1
   else
       ZERO   0
   endif
   PC    PC + 1

Vous allez pouvoir comprendre les cas particuliers présentés maintenant.

Structure si-alors-sinon[modifier | modifier le wikicode]

En écriture en langage C cette structure de contrôle test si sinon s'écrit :

if (s0==s1) {
	// partie alors
}
else{
	// partie autrement
}

En assembleur PicoBlaze cela s'écrit :

	compare s0,s1
	jump nz, branche_autrement
	;code pour la branche alors
	jump if_fini
branche_autrement:
	;code pour la branche autrement
if_fini:
	;code suivant le if

Pour les autres sauts conditionnels voir dans la liste des instructions.

Structure répéter ... tant que ... et structure tant que...[modifier | modifier le wikicode]

On donne la structure correspondante dans plusieurs cas particuliers. Remarquez les difficultés pour le passage de s0>=5 à s0>5.

; tant que s0==s1 faire action
tque:	 COMPARE s0,s1 	
	 JUMP NZ, suite ; s0-s1<>0 on va a suite
	 ;action qui modifie s0 et/ou s1	
	 JUMP tque
suite:	....

La comparaison suivante est basée sur le fait qu'une comparaison est équivalente à une soustraction et qu'ainsi le drapeau C est positionné si et seulement si le résultat de la soustraction est strictement négatif. Attention ce résultat général n’est pas intuitif.

; tant que s0>=5 faire action
tque:	 COMPARE s0,5 ; si s0-5<0 alors C=1	
	 JUMP C, suite ; s0-5<0 on va a suite
	 ;action qui modifie s0 	
	 JUMP tque
suite:	....

La comparaison suivante est plus complexe à réaliser car aucune instruction de saut n'est conditionnée par les deux drapeaux Z et C simultanément. Un moyen d'y parvenir est d'inverser la comparaison ce qui nécessite de mettre 5 dans un registre !

; tant que s0>5 faire action
	LOAD s1,5 ; obligatoire pour inverser comparaison
tque:	COMPARE s1,s0 	
	JUMP NC, suite ; s0-5<=0 on va a suite
	;action qui modifie s0 	
	JUMP tque
suite:	....

Structure for ...[modifier | modifier le wikicode]

En utilisant le plus possible les définitions symboliques on peut réaliser ce type de boucle avec un programme du type :

; pour i variant de 1 à 15 
;(compris) faire action
constant MAX, 15
namereg s0,i
  LOAD i, MAX
boucle:
  ;code de boucle
  ...
  SUB i,01
  JUMP NZ,boucle
  ;code suivant la boucle
  ...

L'adressage indexé[modifier | modifier le wikicode]

Indexé : l'adresse est calculée par le contenu du registre sX utilisé dans l'adressage. Ce registre est mis entre parenthèses dans ce cas. L'espace mémoire étant limité à 64 octets, seuls les 6 bits de poids faibles sont utilisés dans ce mode d'adressage.

	00205	LOAD s2,05		;boucle 5 fois (s2 compteur de boucle)
0188	07010	FETCH s0,(s1)	;charge le contenu donné par s1 dans s0
	18300	ADD s3,s0		;additionne 5F en hexadécimal
	18101	ADD s1,01		;permet d'aller à l'adresse suivante
			SUB S2,01		;s2 <- s2-1
	34188	JUMP NZ,188	;saute à l'adresse 188 (sur {{Unité|10|bits}})

Les sous-programmes le switch et le PicoBlaze[modifier | modifier le wikicode]

Les sous-programmes[modifier | modifier le wikicode]

Les instructions CALL et RETURN sont destinées à l'appel de sous-programme et son retour. Expliquer le rôle de la pile pour les appels imbriqués. La pile ne comporte que 31 niveaux est n'est manipulable par aucune instruction spécifique. Ceci est inconvénient pour les langages évolués qui ne peuvent pas réaliser le passage de paramètres en utilisant la pile. Cela veut aussi dire qu’il n'y aura donc pas d'initialisation de la pile en début de programme.

La programmation de suivant le cas[modifier | modifier le wikicode]

La programmation du test selon s'écrit en C :

switch (variable) { /* variable est de type int ou char */
case valeur_1 : action_1; break;
case valeur_2 : action_2; break;
...
case valeur_n : action_n; break;
default : action_0;
}


Voici comment tout ceci se met en forme en assembleur PicoBlaze :

constant valeur_1, ...
constant valeur_2, ...
...
constant valeur_n, ...

	compare s0, valeur_1
	jump nz case_2
	...; code pour action_1
	jump case_done
case_2:
	compare s0, valeur_2
	jump nz case_3
	...; code pour action_2
	jump case_done
case_3:
	...; etc

case_n:
	compare s0, valeur_n
	jump nz default
	...; code pour action_n
	jump case_done
default:
	...; code pour action_0
case_done:
	...; code suivant le case


Nous allons maintenant donner des exemples de morceau de code afin d'améliorer l'apprentissage de l'assembleur du picoBlaze.

Code utile pour le PicoBlaze[modifier | modifier le wikicode]

Nous allons donner un peu de code pour les personnes intéressées par développer du code.

Triple boucle d'attente[modifier | modifier le wikicode]

Voici un code qui réalise un sous-programme d'attente d'environ une seconde :

constant MAX, A0
NAMEREG s0, i 
NAMEREG s1, j 
NAMEREG s2, k 
;......
;......
;=== triple boucle d'attente=== 
wait:
	LOAD i,MAX
loop:	
		LOAD j,MAX
loop_1:
			LOAD k,MAX
loop_2:
			SUB k,01
			JUMP NZ, loop_2
		SUB j,01
		JUMP NZ, loop_1
	SUB i,01
	JUMP NZ,loop
	RETURN

Augmenter la valeur A0 augmentera la période.

incrémentation et décrémentation BCD[modifier | modifier le wikicode]

Lorsqu'on désire sortir le résultat d'un compteur sur des afficheurs sept segments il est intéressant d’avoir un compteur BCD. Voici un sous-programme qui réalise cela :

NAMEREG s5, cmpt ; compteur à afficher
NAMEREG s9, test
;......
;......
;=== incrémente nb en BCD ===
incrementBCD: ;incrémente cmpt en BCD
	LOAD test,cmpt ; on ne bouge pas cmpt : test <- cmpt
	AND test,0F
	COMPARE test,09
	JUMP NZ,suite_3
	ADD cmpt,06 ; 06 classique en BCD
suite_3:
	ADD cmpt,01
	COMPARE cmpt,A0
	JUMP NZ,suite_4
	LOAD cmpt,00
suite_4:
	RETURN

Voici le petit frère qui décrémente :

NAMEREG s5, cmpt ; compteur à afficher
NAMEREG s9, test
;......
;......
;=== decrémente nb en BCD ===
decrementBCD: ;decrémente nb en BCD
	LOAD test,cmpt ; on ne bouge pas nb : test <- nb
	AND test,0F
	JUMP NZ,suite_5
	SUB cmpt,06 ; 06 classique en BCD
suite_5:
	SUB cmpt,01
	COMPARE cmpt,F9
	JUMP NZ,suite_6
	LOAD cmpt,99
suite_6:
	RETURN

Une fois cela calculé, il faut l'afficher et cela commence par un transcodage.

Transcodeur[modifier | modifier le wikicode]

Il n’est pas possible de mettre dans ce morceau de code tous les transcodages possibles et imaginables.

Le code donné dans cette section nécessite les déclarations :

constant MAX, FF
namereg s0,i
NAMEREG s1, octet_lsb
NAMEREG s3, s7seg  ; rename register s3 as “s7seg”
NAMEREG s4, Aff

Tout commence par une initialisation. Ici on donne du code pour un afficheur sept segment en hexadécimal avec un '1' pour éteindre le segment.

La première ligne d'initialisation qui est sensée afficher '0' nous montre que le segment 'g' est poids faible.

debut:
        ; initialisation RAM : table de conversion
        LOAD s0,01
        STORE s0,00 
        LOAD s0,4F
        STORE s0,01
        LOAD s0,12
        STORE s0,02
        LOAD s0,06
        STORE s0,03 
        LOAD s0,4C
        STORE s0,04 
        LOAD s0,24
        STORE s0,05
        LOAD s0,20
        STORE s0,06
        LOAD s0,0F
        STORE s0,07
        LOAD s0,00
        STORE s0,08 
        LOAD s0,04
        STORE s0,09
        LOAD s0,08
        STORE s0,0A
        LOAD s0,60
        STORE s0,0B
        LOAD s0,31
        STORE s0,0C 
        LOAD s0,42
        STORE s0,0D
        LOAD s0,30
        STORE s0,0E
        LOAD s0,38
        STORE s0,0F     

Une boucle infinie peut utiliser cela :

boucle: 
        ;entree des Unité 
        INPUT octet_lsb,0
        AND octet_lsb,0F
        ;conversion par RAM
        FETCH s7seg,(octet_lsb)
        ;sortie 7 segs
        OUTPUT s7seg,0
        LOAD Aff,FE
        ;sélection afficheur sur PORT différent ne marche donc pas avec module PMOD
        OUTPUT Aff,1
        CALL wait

Maintenant au tour des poids forts et la fin de la boucle :

        ; maintenant poids forts
        INPUT octet_lsb,0 ; relecture
        SR0 octet_lsb
        SR0 octet_lsb
        SR0 octet_lsb
        SR0 octet_lsb
        AND octet_lsb,0F ;inutile en principe
        ;conversion par RAM
        FETCH s7seg,(octet_lsb)
        ;sortie 7 segs
        OUTPUT s7seg,0
        LOAD Aff,FD
        ;sélection afficheur
        OUTPUT Aff,1
        CALL wait
        JUMP boucle

Une routine d'attente même trop rapide peut fonctionner :

wait:
        LOAD i,MAX
loop:   SUB i,01
        JUMP NZ,loop
        RETURN

La partie matérielle du PicoBlaze[modifier | modifier le wikicode]

Embarquer le PicoBlaze[modifier | modifier le wikicode]

Le PicoBlaze est un microcontrôleur destiné à être embarqué dans un FPGA. Ce qui caractérise ce type de composant est qu’il comporte en général peu de logique d'interfaçage vers l'extérieur (PORTS) et encore moins de logique interne traditionnelle (timer, comparaison, capture, ...). La raison à cela est qu’il va se trouver dans un FPGA où le développement de cette logique externe se fait très facilement. On peut donc l'adapter à notre propre problème. Cette absence de ports va rendre l'architecture de ce micro-contrôleur facile à comprendre. Commençons par présenter l'aspect fonctionnel du PicoBalze :

Entrées et sorties du PicoBlaze

L'article Wikipédia sur le PicoBlaze vous donne l'entité équivalente à cette figure en VHDL.

La plus petite application consiste à lui ajouter une mémoire morte avec les instructions correspondantes dedans. Cela nous donne le schéma ci-dessous où toutes les entrées ainsi que les sorties seront reliées à de la logique du FPGA comme cela sera partiellement détaillé dans la section suivante.

Remarquez dans cette figure que la ROM est synchrone et qu'elle utilise la même horloge que le PicoBlaze.

Les entrées sorties[modifier | modifier le wikicode]

Nous allons apprendre dans ce chapitre à interfacer des ports au PicoBlaze. Commençons par les entrées.

Interfacer un PORT d'entrée[modifier | modifier le wikicode]

Le diagramme temporel correspondant est présenté maintenant.

Chronogramme de lecture d'un PORT d'entrée

Pour faire l'acquisition d'une valeur externe, il faut réaliser une instruction « INPUT » en précisant un numéro de port sur 8 bits. Il y a donc 256 ports possibles.

Panneau d’avertissement Le schéma donné ci-dessous est incorrect et sera corrigé plus tard. Il ne faut pas utiliser le signal "read_strobe" pour une lecture de PORT sauf dans le cas où l’on a un FIFO en entrée si l’on en croit la documentation Xilinx !!! La section suivante, par contre, est correcte : utilisation de "write_strobe" pour une écriture dans un PORT. Cela nous montre la dissymétrie entre une entrée et une sortie.

On remarque que l'instruction « INPUT » est réalisée en deux fronts d'horloge (comme toutes les instructions) et qu'un signal READ_STROBE est disponible sur un seul front d'horloge. La partie matérielle peut être réalisée comme suit :

Décodage d'un PORT en entrée

Dans cet exemple on dispose de 4 registres 8 bits pour lesquels on n'a pas dessiné les entrées arrivant de l'extérieur. Le multiplexeur ainsi que la logique combinatoire de décodage peuvent n'utiliser que les 2 bits de poids faible du signal PORT_ID. Il est possible de rajouter un registre entre le multiplexeur et l'entrée du PicoBlaze pour augmenter les performances (pipeline).

On donne un exemple de programme VHDL correspondant aux deux composants ajoutés, le multiplexeur et la logique de décodage ainsi qu'une partie du registre.

-- multiplexeur 
with port_id(1 downto 0) select 
  data <= indata_0 when "00", 
		indata_1 when "01", 
		indata_2 when "10", 
		indata_3 when "11"; 

-- circuit décodage 
process(read_strobe,port_id) begin 
  if read_strobe='0' then 
    rv <= "0000"; 
  else 
    case port_id(1 downto 0) is 
      when "00" => rv <= "0001"; 
      when "01" => rv <= "0010"; 
      when "10" => rv <= "0100"; 
      when others => rv <= "1000"; 
    end case; 
  end if; 
end process;

-- registre {{Unité|8|bits}}
-- A mettre dans une architecture avec l'entité correspondante
-- car on l’utilisera 4 fois
process(clk) begin
  if clk'event and clk='1' then
    if rd = '1' then
      r_data <= d;
    end if;
  end if;
end process;

Interfacer un PORT de sortie[modifier | modifier le wikicode]

Le diagramme temporel de l'instruction OUTPUT est donnée ci-dessous.

Chronogramme de l'écriture dans un PORT

On n'a pas représenté les signaux de sorties du circuit de décodage mais ils sont légèrement décalés par rapport à WRITE_STROBE puisqu’ils l'utilisent. Nous montrons ci-dessous un exemple de circuit pouvant réaliser une sortie sur un des quatre ports présents :

Comment gérer plusieurs PORTs en sortie

Pour quatre ports seuls les deux bits de poids faible de PORT_ID[7:0] peuvent être utilisés. On donne pour une meilleure compréhension les programmes VHDL correspondants :

-- circuit décodage 
process(write_strobe,port_id) begin 
  if write_strobe='0' then 
    en_d <= "0000"; 
  else 
    case port_id(1 downto 0) is 
      when "00" => en_d <= "0001"; 
      when "01" => en_d <= "0010"; 
      when "10" => en_d <= "0100"; 
      when others => en_d <= "1000"; 
    end case; 
  end if; 
end process;

Remarquons qu’il est possible de simplifier largement le décodage si on utilise comme numéros de PORT : 1, 2, 4 et 8 (au lieu de 1, 2, 3 et 4) et du coup les 4 bits de poids faible de PORT_ID[7:0] :

-- circuit décodage simplifié
process(write_strobe,port_id) begin 
  if write_strobe='0' then 
    en_d <= "0000"; 
  else 
    en_d <= port_id(3 downto 0); 
  end if; 
end process;

Un autre problème va nous intéresser maintenant : les interruptions.

Les interruptions[modifier | modifier le wikicode]

La gestion des interruptions est en général très dépendante de l’application à réaliser. Encore une fois le PicoBlaze ne comprend par défaut que le minimum de logique pour gérer les interruptions. Quand on dit minimum, c’est le minimum : même pas de gestion de timer par exemple. C'est à l'utilisateur de réaliser la logique correspondante chargée de réaliser le seul signal « INTERRUPT » présent (une seule source d'interruption donc par défaut). L'activation de ce signal force le PicoBlaze à réaliser un saut vers l'adresse 3FF (dernière adresse de la mémoire programme). À noter qu'après un RESET le signal d'interruption est inactif : une instruction « ENABLE INTERRUPT » doit être réalisée pour l'activer. À noter que l'exécution d'une interruption préserve les drapeaux C et Z du registre d'état.

Partie logicielle[modifier | modifier le wikicode]

L'architecture d'un programme assembleur gérant une interruption sera comme :

Exécution d'une interruption dans un programme

Lorsqu'une interruption (1) arrive, le programme est dérouté (2) à l'adresse 3FF. Cette adresse branche (3) à la routine d'interruption proprement dite. Puis quand la routine est terminée on revient là où l’on a été interrompu (4).

Chronogramme[modifier | modifier le wikicode]

La routine d'interruption n’est pas instantanée dès réception du signal d'interruption. L'iinstruction en cours continue à s'exécuter puis une fois terminée l’ensemble des opérations décrites dans la section précédente sont synchronisées avec l'horloge comme indiqué sur le chronogramme suivant :

Chronogramme d'une interruption

Partie matérielle[modifier | modifier le wikicode]

Un exemple simple comprenant une seule source d'interruption est présenté maintenant.

Les signaux SET et RESET sont asynchrones.

Panneau d’avertissement Le schéma ci-dessus trouvé dans la documentation Xilinx ne fonctionne pas correctement si la durée du signal d'interruption est trop courte (et si la bascule D comporte une horloge). Nous remplacerons ce schéma à l’occasion en faisant disparaître ce bandeau.

L'entrée "interrupt" du picoBlaze est active sur niveau haut. Les interruptions doivent être autorisées à l'intérieur du programme avec une instruction "ENABLE INTERRUPT" pour que cette entrée soit prise en compte. Il est bon de se souvenir aussi que quand le picoBlaze commence l'exécution d'une interruption, il désactive automatiquement les interruptions. Cela restera ainsi sauf si votre retour d'interruption est réalisé avec un "RETURNI ENABLE" comme instruction.

Voici un morceau de code VHDL donné par Ken Chapman dans le forum picoBlaze pour gérer les interruptions :

interrupt_control: process(clk)
begin
  if clk'event and clk='1' then
    -- Detect rising edge and generate a pulse
    extint_delay <= extint;
    if (extint = '1' and extint_delay = '0') then
      interrupt_event <= '1';
     else
      interrupt_event <= '0';
    end if;

   -- Drive interrupt until acknowledged by PicoBlaze
   if interrupt_event = '1' then
     interrupt <= '1';
    else
     if interrupt_ack = '1' then
       interrupt <= '0';
      else
       interrupt <= interrupt;
     end if;
   end if; 
  end if;
end process interrupt_control;

Ce code ne fonctionne correctement que si le signal "extint" est synchrone.

Voir aussi[modifier | modifier le wikicode]

Liens internes[modifier | modifier le wikicode]

liens externes[modifier | modifier le wikicode]

  • copyBlaze un clone du picoBlaze qui supporte une interface de type Wishbone. Deux instructions ont été ajoutées au picoBlaze pour gérer le bus wishbone.
  • Assembleur picoBlaze donne des indications sur la façon d’utiliser data2mem puisqu’un fichier BMM est donné.
  • pbcc un compilateur C pour picoBlaze dérivé de sdcc. C'est une bonne nouvelle.
  • « mcx16 » (ArchiveWikiwixQue faire ?). Consulté le 2014-09-19 un clone du picoBlaze sur 16 bits

Exercices[modifier | modifier le wikicode]

Exercice 1[modifier | modifier le wikicode]

Soit le programme suivant :

library ieee;
use ieee.std_logic_1164.all;
use ieee.std_logic_arith.all;
use ieee.std_logic_unsigned.all;


entity top is port (
	entrees : in std_logic_vector(7 downto 0);
	sorties : out std_logic_vector(7 downto 0);
	clk,reset : in std_logic
	);
end entity;

architecture behavior of top is

component kcpsm3 is
    Port (      address : out std_logic_vector(9 downto 0);
            instruction : in std_logic_vector(17 downto 0);
                port_id : out std_logic_vector(7 downto 0);
           write_strobe : out std_logic;
               out_port : out std_logic_vector(7 downto 0);
            read_strobe : out std_logic;
                in_port : in std_logic_vector(7 downto 0);
              interrupt : in std_logic;
          interrupt_ack : out std_logic;
                  reset : in std_logic;
                    clk : in std_logic);
end component;

component mpu_rom is
    Port (      address : in std_logic_vector(9 downto 0);
            instruction : out std_logic_vector(17 downto 0);
                    clk : in std_logic);
    end component;

signal address : std_logic_vector(9 downto 0);
signal instruction : std_logic_vector(17 downto 0);
signal ws,rs: std_logic;
signal s_sorties,s_entrees: std_logic_vector(7 downto 0);


begin

ic1 : kcpsm3 port map ( 
			address => address,
			instruction => instruction,
			port_id => open,
			write_strobe => ws,
			out_port => s_sorties,
			read_strobe => rs,
			in_port => s_entrees,
			interrupt => '0',
			interrupt_ack => open,
			reset => reset,
			clk => clk);
ic2 : mpu_rom port map (
			address=> address,
			instruction=> instruction,
			clk=> clk);

-- bascule D en entree
process(clk) begin
	if rising_edge(clk) then
		if rs='1' then
			s_entrees <= entrees;
		end if;
	end if;
end process;

-- bascule D en sortie
process(clk) begin
	if rising_edge(clk) then
		if ws='1' then
			sorties <= s_sorties;
		end if;
	end if;
end process;
			
end behavior;

Réaliser le schéma correspondant à cette architecture.

Exercice 2[modifier | modifier le wikicode]

Faites les exercices de : Travail_pratique/TP 4.

ANNEXE[modifier | modifier le wikicode]

Nous avons utilisé kpicosim pour les TPs... mais kpicosim ne s'installe plus sur les nouvelles versions de Linux. Voici donc un autre moyen de générer la ROM pour picoBlaze.

Utiliser picoasm pour générer la ROM VHDL prog_rom.vhd[modifier | modifier le wikicode]

picoasm est un assembleur non graphique. Son code source se trouve facilement sur internet. Il tourne donc sur les nouvelles versions de Linux (contrairement à kpicosim) et probablement sous Windows. S'il est installé dans un sous-répertoire "soft" du projet picoBlaze, il se lance avec la commande :

./picoasm -i prog1.psm -t ROM_form.vhd -m prog_rom -d ../

Il nécessite, comme son cousin graphique kpicosim, un fichier de modèle appelé ici "ROM_form.vhd" et génère un fichier "prog_rom.vhd".