Bibliothèque d'IPLe processeur Homade ne fonctionne que si vous lui ajoutez des
IPs adaptés à votre algorithme. Ces Ips sont de deux
types:
Un certain nombre d'IPs sont disponibles dans la bibliothèques.
le lien entre numéro de l'IP et le fichier VHDL qui réalise l'IP
doit être exact, ce numéro est codé sur 11 bits. Le fichier
IPcode.vhd est donné à titre d'exemple et peu bien sur
être personnalisé. Ce numéro est également utilisé par
l'assembleur Homade.
Les IPs sont les entités matérielles qui vont permettre de
traiter, modifier les données manipulées. Homade seul ne propose
que l'infrastructure nécessaire à la gestion du contrôle
de flot et le système de pile qui permet d’échanger et de
stocker des données entre deux traitements. Pour le reste TOUS
les IPs qui seront nécessaires pour réaliser votre application
doivent être ajoutés au processeur Homade. Il existe deux
techniques d'ajout qui se font dans le fichier HSM.vhd,
| Nom asssembleur |
Hexa 16 |
POP |
Push |
IPcode proposé |
définition |
| dup |
B008 |
01 |
10 |
00000001&000 |
DUP ( a -- a a ) :
duplique le sommet de la pile |
| swap |
D009 |
10 |
10 |
00000001&001 |
SWAP ( a b -- b a ) :
permutation du sommet et du sous_sommet |
| tuck |
D80A |
10 |
11 |
00000001&010 |
TUCK ( a b -- b a b )
: recopie et insère le sommet sous le sous-sommet |
| over |
D80B |
10 |
11 |
00000001&011 |
OVER ( a b -- a b a ) :
recopie et empile le sous-sommet |
| rot |
F80C |
11 |
11 |
00000001&100 |
ROT ( a b c -- b c a ) : Rotation des trois valeurs en tête de pile dans le sens des aiguilles d'une montre |
| invrot |
F80D |
11 |
11 |
00000001&101 |
INVROT ( a b c -- c a b ) : Rotation des trois valeurs en tête de pile dans le sens inverse des aiguilles d'une montre |
| nip |
C80E |
10 |
01 |
00000001&110 |
NIP ( a b -- b) :
supprime le sous-sommet |
| 00000001&111 |
Not used yet |
| Nom asssembleur |
Hexa 16 |
POP |
Push |
IPcode proposé |
définition |
| nop |
8000 |
00 |
00 |
00000000000 |
1 cycle pour rien |
| push1 |
8800 |
00 |
01 |
00000000000 | push1( - 0) empile un 0 |
| push2 |
9000 |
00 |
10 |
00000000000 | push2( - 0 0) empile deux 0 |
| push3 |
9800 |
00 |
11 |
00000000000 | push3( - 0) empile trois 0 |
| pop1/drop |
A000 |
01 |
00 |
00000000000 | pop1(a -) dépile une valeur
de la pile idem pour drop |
| pop2 |
C000 |
10 |
00 |
00000000000 | pop2(a b -) dépile deux valeurs de la pile |
| pop3 |
E000 |
11 |
00 |
00000000000 | pop3(a b c -) dépile trois valeurs de la pile |
| 00000000000 | Toute autre combinaison de
push et pop avec le même IPcode |
Cet IP fonctionne comme une pile, il fonctionne en un seul
cycle, donc avec un numéro d'IP du format '0xxxxxxxxxx'.
Il est indispensable d'instancier cet IP si l'on veut utiliser
des structures de contrôles imbriquées. En particulier le DO
LOOP que nous proposons dans l'assembleur structuré utilise cet
IP. Par contre si c'est pas utile pour vous, inutile de le
garder dans le code synthétisé, il suffit juste de mettre le
code en commentaire dans le fichier HSM.vhd;-)
voici l'entity de cet IP.
entity
IP_datastack is
GENERIC (Mycode : std_logic_vector(9 downto
0) );
Port ( clk : in STD_LOGIC;
clr : in STD_LOGIC;
Tin : in STD_LOGIC_VECTOR (31 downto
0);
Tout : out STD_LOGIC_VECTOR (31 downto 0);
IPcode : in STD_LOGIC_VECTOR(10 downto 0));
end
IP_datastack;
On n’utilise qu’un seul bus en entré et en sortie. Ils sont
connectés au sommet de pile. Ici nous avons aussi un
signal de clock clk et de reset clr
car l'IP utilise un registre réalisé avec des
Flip/Flop pour gérer le sommet de pile local et garantir
un temps d'exécution de moins d'un cycle, le reste de la pile
utilisera une mémoire du FPGA. Mycode est codé sur 10
bits, ce sont les 10 bits de poids forts de IPcode. Le 11ieme
bit sera utiliser pour déclencher soit un DataPush
'1' soit un DataPop '0'. C'est la valeur du sommet de pile
qui sera utilisée et éventuellement supprimée si les bits
de POP valent 01. ( en laissant ces bits à 00 on peut
ranger la valeur du sommet dans la DataStack sans la supprimer
de la tête de pile de l'Homade) . L'ipcode(10:1) par
défaut est "0000000011". La data stack a une profondeur de 16
mots.
| Nom asssembleur |
Hexa 16 |
Pop |
Push |
IPcode proposé |
définition |
| DataPop |
8806 |
00 |
01 |
0000000011&0 |
DataPop ( - a) dépile de la
data stack pour empiler sur la stack Homade |
| DataPush |
A007 |
01 |
00 |
0000000011&1 | DataPush(a -) dépile la
stack Homade vers la data stack |
| DataPushDup |
8007 |
00 |
00 |
0000000011&1 | DataPushDup( - ) copie le
sommet Hstack Homade sur la data stack |
En cours de développement . il faudra placer sur la pile le
numéro du registre sur une valeur pour écriture. on
récupère une donnée en sommet de pile en lecture. A priori ça
doit se faire en un seul cycle. Le code "0000001000"
&'0'/'1' est déjà mis en place dans Homade , il reste a
développer l'IP en vhdl. On pourra instancier 8 ou 16 ou 2n
registres
| Nom asssembleur |
Hexa 16 |
Pop |
Push |
IPcode proposé |
définition |
| RegLd |
A811 |
01 |
01 |
0000001000&0 |
RegSt( a - REG(a) ) empile
la valeur du registre a |
| RegSt |
C010 |
01 |
00 |
0000001000&1 | RegSt(a b -) REG(a) <= b |
| RegStDup |
A010 |
00 |
00 |
0000001000&1 | RegStDup( a b - a ) REG(a)
<= b |
Voici un bel exemple d'intégration d'un IP existant dans le
cœur Homade. Nous avons repris l'ALU 16 bits développé
pour le FC16 et nous l'avons transformée en 32 bits, processeur
Forth développé par R.
Haskell. Le code funit.vdh n'a été que légèrement modifié
, en particulier pour y ajouter une instruction fortement
utile pour manipuler des chaines de 12 bits ( cf l'IP SwapAnd et
l'instruction LIT).
Afin d'intégrer ce code, un Wrapper est défini afin de réaliser
une Entity compatible avec l'architecture Homade. Cette ALU
fonctionne en moins de un cycle et donc là encore sont numéro
doit être de la forme '0xxxxx'. Les 5 bits de poids faibles de
l'IPcode servent au sein de l'IP à choisir la bonne opération à
effectuer.
Voici le code Wrapper:
entity
IP_Funit is
GENERIC (Mycode : std_logic_vector(5 downto
0) );
Port (
Tin : in
STD_LOGIC_VECTOR (31 downto 0); -- 3
entrées
Nin : in STD_LOGIC_VECTOR (31 downto 0);
N2in : in STD_LOGIC_VECTOR (31 downto 0);
IPcode : in STD_LOGIC_VECTOR (10 downto 0);
Tout : out STD_LOGIC_VECTOR (31 downto 0);
-- 2
sorties
Nout : out STD_LOGIC_VECTOR (31 downto 0));
end
IP_Funit;
architecture
Behavioral of IP_Funit is
signal
ResA,ResB :
std_logic_vector(31 downto 0) := (others =>'Z');
--
-- le compsant que l'on desire wrapper
--
COMPONENT funit32
PORT(
a : IN std_logic_vector(31 downto 0);
b : IN std_logic_vector(31 downto 0);
c : IN std_logic_vector(31 downto 0);
fcode : IN std_logic_vector(4 downto
0);
y : OUT std_logic_vector(31 downto 0);
y1 : OUT std_logic_vector(31 downto 0)
);
END COMPONENT;
begin
Inst_funit32: funit32 PORT MAP(
a => Tin,
b => Nin,
c => N2in,
fcode => IPcode (4 downto 0) ,
y => ResA,
y1 => ResB
);
--
-- On connecte les sorties de funit a nos sorties si le IPcode
est le bon
--
Tout <= ResA when IPcode(10 downto 5) = Mycode else (others
=>'Z');
Nout
<= ResB when IPcode(10 downto 5) = Mycode else (others
=>'Z');
end
Behavioral;
Voici la liste des IPs réalisés par funit
| Nom assembleur |
Hexa 16 bits |
Pop |
Push |
Ipcode proposé |
Défintion |
| add |
C820 |
10 |
01 |
000001&00000 |
Add ( a b - a+b ) : empile
la somme du sommet + sous sommet le meme Ip peut
être utilisé avec Pop = 00... |
| minus |
C821 |
10 |
01 |
000001&00001 |
Minus ( a b - ( a-b ) )
ss_sommet - sommet |
| inc |
A822 |
01 |
01 |
000001&00010 |
Inc ( a - a+1 ) |
| dec |
A823 |
01 |
01 |
000001&00011 |
Dec ( a - a-1) |
| not |
A824 |
01 |
01 |
000001&00100 |
Not ( a - not a ) |
| and |
C825 |
10 |
01 |
000001&00101 |
And ( a b - a and b ) |
| or |
C826 |
10 |
01 |
000001&00110 |
Or ( a b - a or b ) |
| xor |
C827 |
10 |
01 |
000001&00111 |
Xor ( a b - a xor b ) |
| 2* |
A828 |
01 |
01 |
000001&01000 |
2* : ( a - a(30 .. 0)
& '0' ) |
| Hu2/ |
A829 |
01 |
01 |
000001&01001 |
HU2/ ( a - '0' &
a(31 .. 1) ) |
| H2/ |
A82A |
01 |
01 |
000001&01010 |
H2/ ( a - a(31) &
a(31 .. 1) ) |
| H-> |
C82B |
10 |
01 |
000001&01011 |
-> ( a b - SHR(a, b ) )
rshift a de b bits ss sommet par sommet bits |
| H<- |
C82C |
10 |
01 |
000001&01100 |
<- ( a b - SHL(a, b) )
lshift a de b bits |
| 000001&01101 |
Mpp : multiplication
partielle |
||||
| 000001&01110 |
Shldc : division partielle |
||||
| Comp2 |
A82F |
01 |
01 |
000001&01111 |
Comp2 ( a - not(a) + 1)
complément à 2 |
| Vrai |
8830 |
00 |
01 |
000001&10000 |
Vrai( - x"FFFFFFF") |
| Faux |
8831 |
00 |
01 |
000001&10001 |
Faux ( - x"00000000") |
| Ez |
A832 |
01 |
01 |
000001&10010 |
Ez ( a - si a=Faux
alors Vrai sinon Faux) |
| Neg |
A833 |
01 |
01 |
000001&10011 |
Neg ( a - si a(31)
='1' alors Vrai sinon Faux) |
| GtU |
C834 |
10 |
01 |
000001&10100 |
GtU ( a b - si b>a alors Vrai sinon Faux) |
| LtU |
C835 |
10 |
01 |
000001&10101 |
LtU ( a b - si b<a alors Vrai sinon Faux) |
| Eq |
C836 |
10 |
01 |
000001&10110 |
Eq ( a b - si b=a alors Vrai sinon Faux) |
| GeU |
C837 |
10 |
01 |
000001&10111 |
GeU ( a b - si b>=a alors Vrai sinon Faux) |
| LeU |
C838 |
10 |
01 |
000001&11000 |
LeU ( a b - si b<=a alors Vrai sinon Faux) |
| Ne |
C839 |
10 |
01 |
000001&11001 |
Ne ( a b - si b<>a alors Vrai sinon Faux) |
| Gt |
C83A |
10 |
01 |
000001&11010 |
Gt ( a b - si signed(b)>signed(a) alors Vrai sinon Faux) |
| Lt |
C83B |
10 |
01 |
000001&11011 |
Lt ( a b - si signed(b)<signed(a) alors Vrai sinon Faux) |
| Le |
C83C |
10 |
01 |
000001&11100 |
Ge ( a b - si signed(b)>=signed(a) alors Vrai sinon Faux) |
| Ge |
C83D |
10 |
01 |
000001&11101 |
Le ( a b - si signed(b)<=signed(a) alors Vrai sinon Faux) |
| shiftLit |
C83E |
10 |
01 |
000001&11110 |
ShiftLit (a b - b(19:0)
& a(11:0) remplace ShiftAnd: vérifiez la sémantique pour mettre à jour |
| 000001&11111 |
not used yet!!!!! |
Certains IPs sont proposés afin de gérer les I/O de la carte
nexys3. les mêmes IP seront à proposer pour les cartes
xilinx qui supporteront Homade. Ces IPs permettent de lire une
valeur 8 bits sur les switches , d'afficher une valeur 8 bits
sur les Led et d'afficher une valeur 16 bits sur l'afficheur 7
segments. Par défault les IPcode ont été choisis à
"00000000100", "00000000011" et "00000000010" réciproquement.
| Nom assembleur |
Hexa 16 bits |
Pop |
Push |
Ipcode proposé |
Défintion |
| Led |
A003 |
01 |
00 |
00000000011 |
Led ( a - ) LED <=
a(7:0) |
| LedDup |
8003 |
00 |
00 |
00000000011 | LedDup ( a - a ) LED <= a(7:0) |
| Switch |
8804 |
00 |
01 |
00000000100 | V1.4 Switch ( - a ) a <= si SWITCH'7)='1' alors x"FFFFFF"& SWITCH (7:0) sinon x"000000"& SWITCH (7:0) >V1.4 Switch ( - a) a <= SWITCH(7:0) cf Comp2 |
| Bufout |
A002 |
01 |
00 |
00000000010 | Bufout ( a - )
a(15:0) s'affiche sur les 4 afficheurs 7 segements |
| BufoutDup |
8002 |
00 |
00 |
00000000010 | BufoutDup ( a - a) a(15:0) s'affiche sur les 4 afficheurs 7 segements |
la gestion des boutons est réalisée par un IP long donc
plus d'un cycle. la fin de l'IP est déclenché quand le bon
bouton a été relâché. On propose par défaut l'IPcode
'"10000000010"
| Nom assembleur |
Hexa 16 bits |
Pop |
Push |
Ipcode proposé |
Défintion |
| WaitBtn |
A402 |
01 |
00 |
10000000010 | WaitBtn (a - ) les 4 bits
a(3:0) permettent de définir le masque validant kles
boutons qui seront détectés. L'ip se termine quand l'un
d'entre eux est relâché. '1010' valide BTN3 et BTN1 le fichier ucf associe les boutons de la carte nexys3 dans l'ordre croissant D9 C9 C4 A8 par défaut |
| WaitBtnpush |
AC02 |
01 |
01 |
10000000010 | WaitBtnpush (a - b) en plus du WaitBtn le
numéro du bouton détecté est placé sur la pile ( 00 ou 01
ou 10 ou 11) |
Deux IP existent dans notre bibliothèque. le premier
permet de compter le nombre de cycles entre deux appels
successifs, c'est un shortIP. Le second est une attente active
pour un nombre de cycle précis> 3. Il sont proposés aux deux
IPcode "00000000001" et "10000000001"
| Nom assembleur |
Hexa 16 bits |
Pop |
Push |
Ipcode proposé |
Défintion |
| Tic |
8801 |
00 |
01 |
00000000001 |
Tic( - a ) a recoit le
nombre de cycle entre deux appels successifs à Tic
ou TicRaz puis Tic et remet à zéro le compteur de cycle |
| TicRaz |
8001 |
00 |
00 |
00000000001 | Tic ( - ) remet à zéro le compteur de cycle le compteur vaut 0 au début du programme |
| Wait |
A401 |
01 |
00 |
10000000001 | Wait ( a - ) Ipdone est actif (Ip
terminé) après a cycles. a doit être supérieur à 3. |
Voici quelques idées d'IP à développer et si ils sont validés
ils porront être ajoutés à cette page. Opencores et d autres
sites proposes des codes vhdl que l'on pourrait intégrer dans
Homade.
sur des entiers Multiplcation / division / modulo/puissance : à
priori il faudrait un LongIp
FPU :
unité de calcul en Flotant 32 bits , voir opencore FPU ou les
package vhdl proposés
conversion float entier trunc round etc...
encodeur
binary2bcd, etc...
des
générateurs de nombres aléatoires en un cycle
les
fonctions fonction trigo en fp32
les
fonctions prédefinis des langages de programmation ABS , odd
even min max... entier et flotant
fonction
serie log LN sqrt
Des I/O
nexys3 pour les ports VGA, USB..
factoriel
calcul denombrement Cnp Anp ...
interval
, appartient à ( a b c) b<a<c etc...
calcul
de surface carré rectangle triiangle ( 3 operateurs
ou plus??)
contante
prédéfinies pi , e,
crypto....
nombre premier clef etc???
resolution
ax2 + bx + c en 1 cycle produit X1 et X2
On peut être ammené à construire des IPshort ou des IPlong . voici deux modèles pour celà.
entity IP_add is
GENERIC (Mycode :
std_logic_vector(11 downto 0) );
Port (
-- selectionner
les bus connectés en input aux trois sommets de pile
Tin :
in STD_LOGIC_VECTOR (31 downto 0);
Nin : in STD_LOGIC_VECTOR (31 downto 0);
-- N2in
: in STD_LOGIC_VECTOR (31 downto 0);
-- IPcode permet d'activé les sorties
IPcode : in STD_LOGIC_VECTOR (10 downto 0);
-- séelctionner les trois bus en ouput qui pourraient
produire des valeurs sur les sommets de pile
Tout : out STD_LOGIC_VECTOR (31 downto 0);
--
Nout : out STD_LOGIC_VECTOR (31 downto 0);
--
N2out :
out STD_LOGIC_VECTOR (31 downto 0));
end IP_add;
architecture Behavioral of IP_add is
signal ResA:
std_logic_vector(31 downto 0);
COMPONENT add32bit
PORT(
a : IN
std_logic_vector(31 downto 0);
b : IN
std_logic_vector(31 downto
0);
y : OUT
std_logic_vector(31 downto 0)
);
END COMPONENT;
begin
Inst_fadd32: add32bitPORT MAP(
a => Tin,
b => Nin,
y => ResA
);
Tout <= ResA when IPcode = Mycode else (others =>'Z');
end Behavioral;
voici le chronogramme qu'il faut obtenir pour une exécution en
un cycle. les registre sont mis à jour sur le front descendant.