Le 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,




IP génériques

Les IPs génériques sont souvent nécessaires dans le applications développées sur Homade. Néanmoins cela reste optionnel et l'on peut utiliser ou pas ces IPs. Pour cela il suffit  d'instancier ou pas les IPs prédéfinis dans le code source de l'Homade via le fichier HSM.vhd. Nous avons classés ces IPs par ensemble

IP de gestion de la pile homade

Comme tout processeur à pile, il est souvent nécessaire de manipuler les données qui se trouvent en sommet de pile sans pour autant en changer la valeur.

IP_Stack

Cet ensemble d'IPs regroupe jusque 8 Ips différents. Le regroupement de ces 8 IPs impose un codage des numéro des IPs aligné sur un multiple de 8 et une affectation a priori des 8 numéro successifs à chacun des IPs. Il correspond à l 'entity VHDL suivant:

entity IP_stack is
        GENERIC (Mycode : std_logic_vector(7 downto 0) );
    Port ( 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  : in  STD_LOGIC_vector (10 downto 0);
           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_stack;

Cet IP s'exécute en moins de un cycle . on choisira donc un numéro d'IP en spécifiant lors de l'instanciation un code sur 8 bits commençant par 0 : "0xxxxxxx". Les IPs qui le composent seront alors associés aux codes
["0xxxxxxx000" .."0xxxxxxx111"]
On trouve en entrée  les 3 bus Tin, Nin, N2in qui seront connectés respectivement au Sommet, Sous-Sommet et SousSous-Sommet de la pile Homade. On trouve également IPcode qui sera envoyé par le cœur Homde lorsqu’une instruction IP est décodée. Si les 8 bits de poids Forts de IPcode sont les même que Mycode, l'IP correspondant au numéro des 3 bits de poids faible sera exécuté.
Les sorties sont les 3 bus Tout, Nout, N2out qui transportent les résultats vers  respectivement Sommet, Sous-Sommet et SousSous-Sommet. Ces trois Bus sont à l'état 'Z' si l'IP ne produit pas de résultats sur ce bus.

Les fonctions réalisées son (instancié en 00000001000 -> 00000001111  par défaut):

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

Remarque:
    La fonction DROP sera réalisé par un autre IP.
    Il faut mieux éviter de laisser des numéro d'IP non utilisés!!

IP_pushpop

Cet IP ne fait rien de plus que de dépiler 0, 1, 2 ou 3 valeurs  du sommet de pile suivant la valeur des deux bits de POP. Il peut aussi empiler la valeur x"0000000" 0, 1, 2 ou 3 fois en sommet de pile en fonction des bits de PUSH.
La fonction drop est réalisée par cet IP avec POP= 01 et PUSH = 00.
L’entête est similaire à IP_stack mais il n'y a pas besoin des ports d'entrée Tin, Nin et N2in. Il faudra bien sur l'instancier avec une autre valeur de Mycode. ( on a mis '00000000000'  dans l'homade V1.4 diffusé. ) Le bit de poids fort doit être à '0' car cet IP demande un seul cycle. Vous voyez d'ailleurs qu'il n'y a pas de sortie IPdone pour ces IPs.

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


IP gestion des données

Comme vous l'avez sans doute remarqué, il n'y a pas dans le processeur Homade de "return stack" de "register file" ou de "data memory". Là encore en fonction des besoins de l’application, on instancie lors de la synthèse du processeur les IPs qui vont réaliser ces fonctionnalités.
Nous avons définis 3 IPs qui peuvent être utiles pour cela. Vous pouvez bien entendu remplacer ces IPs par les vôtres c’est toute la souplesse d'Homade!!!

IP_Datastack

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



IP_Regfile

     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

IP_datamem

     En cours de développement. Même pricnipe que pour le Regfile mais ici le temps d'exécutions sera de plus d'un cycle. On doit pouvoir utiliser les mémoires du chip ou externe  avec des temps de réponses différents.

IP ALU

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


IP Nexys3

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)


Gestion du temps

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.



D'autres IP à développer

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

Construction de vos propres IPs

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.