%------------------------------------------------------------------------------ % $Id: document-morpheo-vhdl_generation-fr.tex 95 2008-12-16 16:24:26Z rosiere $ %------------------------------------------------------------------------------ \SEction{Introduction} Ce document est une aide pour les développeur des générateurs de modèles VHDL de \cpu. Il est décomposé en 6 sections : \begin{itemize} \item Dans la section \ref{tree}, nous présentons l'arborescence des répertoires. \item Dans la section \ref{vhdl_declaration}, nous expliciterons l'API ({\it Application Programming Interface}) pour la déclaration des signaux et des types du VHDL. \item Dans la section \ref{vhdl_body}, nous présenterons l'API pour générer le comportement des composants. \item Dans la section \ref{vhdl_structural}, nous expliquerons la manière de créer des sous composants. \item Dans la section \ref{example}, nous montrerons quelques exemples. \item Dans la section \ref{testbench}, nous présenterons la méthodologie de test. \end{itemize} \Section{Arborescence}\label{tree} Dans le répertoire contenant le projet, nous avons les répertoires suivant : \begin{description} \item[IPs/systemC/processor/Morpheo/Documentation/ :] Contient différent document décrivant certain point du projet, dont cette documentation. \item[IPs/systemC/processor/Morpheo/Behavioural/ :] \item[IPs/systemC/processor/Morpheo/Behavioural/include/ :] \begin{description} \item [Parameters.h :] Contient la classe {\it Parameters}, cette classe définit les paramètres constants. \item [Vhdl.h :] Contient la classe {\it Vhdl}. \end{description} \item[IPs/systemC/processor/Morpheo/Behavioural/\dots/Component :] Chaque composant est inclue dans un répertoire spécifique. \item[IPs/systemC/processor/Morpheo/Behavioural/\dots/Component/include :] \begin{description} \item [Parameters.h :] Contient la classe {\it Parameters}, elle dérive de la classe contenu dans le fichier le répertoire {\it \dots/Behavioural/include/}. \item [Component.h :] Contient la classe {\it Component}. Il définit l'interface, les registres ainsi que les méthodes du modèle systemC. \item [Types.h :] Contient les types spéciaux. \end{description} \item[IPs/systemC/processor/Morpheo/Behavioural/\dots/Component/src :] \begin{description} \item [Component\_transition.cpp :] \item [Component\_genMoore.cpp :] \item [Component\_genMealy\_XXX.cpp :] \item [Component\_vhdl\_declaration.cpp :] \item [Component\_vhdl\_body.cpp :] \end{description} \item[IPs/systemC/processor/Morpheo/Behavioural/\dots/Component/Selftest :] \end{description} \Section{VHDL : déclaration}\label{vhdl_declaration} Les déclarations ce font dans le fichier {\it Component\_vhdl\_declaration.cpp}. \subSection{Interfaces} L'interface est définit dans le modèle SystemC. Il n'ont pas nécessaire de la redéfinir pour le modèle VHDL. La nomenclature est la suivante : \begin{itemize} \item La direction en minuscule ({\it in}, {\it out}). \item Le nom de l'interface en majuscule ({\it READ}, {\it PUSH}, \dots). \item Le numéro de l'interface (la première interface aura le numéro 0. S'il n'y a qu'une interface, celle ci aura tout de même le numéro 0). \item Le nom du signal en majuscule ({\it VAL}, {\it ADDRESS}, \dots). \end{itemize} Par exemple pour la 2 ème interface de lecture d'un banc de registre : \begin{verbatim} in_READ_1_VAL : in std_logic; out_READ_1_ACK : out std_logic; in_READ_1_ADDRESS : in std_logic_vector(8 downto 0); out_READ_1_DATA : out std_logic_vector(31 downto 0); \end{verbatim} Chaque composent (aussi bien combinatoire que séquentielle) possède un signal d'horloge et un signal de reset. (Ce dernier est actif à l'état bas). Ils ont tout les deux le même nom quelque soit le composant. \begin{verbatim} in_CLOCK : in std_logic; in_NRESET : in std_logic; \end{verbatim} \subSection{Types} Le type de base utilisé est le {\it std\_logic\_vector} (ou std\_logic si le vecteur est sur un seul bit). Pour cela on utilise la fonction suivante : \begin{verbatim} std::string std_logic (uint32_t size); \end{verbatim} Pour accéder à une partie du vecteur on utilise la fonction {\it std\_logic\_range}. Elle a les prototypes suivant : \begin{verbatim} std::string std_logic_range (uint32_t size, uint32_t max , uint32_t min , bool force=false); std::string std_logic_range (uint32_t max , uint32_t min , bool force=false); std::string std_logic_range (uint32_t size, bool force=false); \end{verbatim} La première fonction fais un test sur la taille que la seconde ne fait pas. L'argument de la troisième définit la taille (ce qui implique que la borne minimum est 0 et la borne maximum est size-1). Toute les surcharges ont le bolléen optionnel {\it force}. S'il est à faux, alors des tests seront effectués sur les bornes et la taille, sinon aucun test n'est effectué. Par exemple : \begin{verbatim} std_logic_range(4,false) renvoie "(3 downto 0)". std_logic_range(1,false) renvoie "(0)". std_logic_range(1,true ) renvoie "(0 downto 0)". \end{verbatim} Pour les types plus complexe, la classe {\it Vhdl} possède une méthode générique. Le premier argument est le nom du type le second est le type. \begin{verbatim} void set_type (std::string name, std::string type); \end{verbatim} L'exemple suivant définit un type représentat un tableau de {\it nb\_word} mots de {\it size\_word} bit chacun : \begin{verbatim} vhdl->set_type ("Tregfile", "array "+std_logic_range(nb_word)+" of "+std_logic(size_word)); \end{verbatim} \subSection{Constantes} La déclaration de constante, ce fait avec la méthode {\it set\_constant} de la classe {\it Vhdl}. Les différentes surcharges de cette méthode est le type des arguments {\it type} et {\it init}. \begin{verbatim} void set_constant (std::string name, std::string type, std::string init); void set_constant (std::string name, uint32_t size, std::string init); void set_constant (std::string name, uint32_t size, uint32_t init); \end{verbatim} Par exemple pour coder les états d'un automate à 5 états en One Hot : \begin{verbatim} vhdl->set_constant ("State_idle",5,1); \end{verbatim} Dans le cas de signaux de type {\it std\_logic}, au lieu de déclarer des constantes, il existe deux fonctions permettant d'utiliser des constantes directement dans le corps du composant. La première est {\it std\_logic\_others}. Elle permet de définir des constantes dont soit les bits sont à pleins un soit à plein zéro. \begin{verbatim} std::string std_logic_others (uint32_t size, bool cst ); \end{verbatim} Pour toute les autres constantes, la méthode {\it std\_logic\_conv} transforme un entier en {\it std\_logic\_vector}. \begin{verbatim} std::string std_logic_conv (uint32_t size, std::string value); std::string std_logic_conv (uint32_t size, uint32_t value); \end{verbatim} \subSection{Signaux internes} Les signaux internes sont définit grâce au méthode {\it set\_signal}. Le premier argument est le nom du signal. Le second est soit un type soit une taille (dans le cas où le type est un {\it std\_logic\_vector}). \begin{verbatim} void set_signal (std::string name, std::string type); void set_signal (std::string name, uint32_t size); \end{verbatim} La méthode est également surchargé si le signal à besoin d'une initialisation. \begin{verbatim} void set_signal (std::string name, std::string type, std::string init); void set_signal (std::string name, uint32_t size, std::string init); void set_signal (std::string name, uint32_t size, uint32_t init); \end{verbatim} En vhdl il est possible de renommer une champ d'un signal. Ceci ce fait à l'aide de la fonction {\it set\_alias}. Elle prend 4 arguments. Le premier étant le nom du signal après le renommage. le second est soit le type, soit la taille du {\it std\_logic\_vector} du signal rénommé. Les deux derniers conserné le signal à renommé : le nom de ce dernier ainsi que l'intervalle. Pour le dernier paramètre il est recommandé d'utilisé la fonction {\it std\_logic\_range}. \begin{verbatim} void set_alias (std::string name1 , std::string type1 , std::string name2 , std::string range2); void set_alias (std::string name1 , uint32_t size1 , std::string name2 , std::string range2); \end{verbatim} \Section{VHDL : comportement}\label{vhdl_body} Le comportement du composant est définit dans le fichier {\it Component\_vhdl\_body.cpp}. Il n'y a pas de fonction aidant à l'écriture du Vhdl. La méthode {\it set\_body} permet de définir une ligne de code VHDL. Un retour à la ligne est automatiquement inséré. Le premier argument est pour l'indentation. \begin{verbatim} void set_body (uint32_t depth, std::string text ); void set_body (std::string text ); \end{verbatim} Pour les commentaires, il y a la fonction {\it set\_comment}. \begin{verbatim} void set_comment (uint32_t depth, std::string text ); void set_comment (std::string text ); \end{verbatim} \Section{VHDL : structurelle}\label{vhdl_structural} \Section{Exemples}\label{example} \subSection{Banc de Registres Monolithique} \subsubSection{Fichier RegisterFile\_Monolithic\_vhdl\_declaration.cpp} \begin{verbatim} void RegisterFile_Monolithic::vhdl_declaration (Vhdl * & vhdl) { vhdl->set_type ("Tregfile", "array " + std_logic_range(_param->_nb_word,true)+ " of "+ std_logic(_param->_size_word)); vhdl->set_signal ("reg_DATA", "Tregfile"); }; \end{verbatim} \subsubSection{Fichier RegisterFile\_Monolithic\_vhdl\_body.cpp} \begin{verbatim} void RegisterFile_Monolithic::vhdl_body (Vhdl * & vhdl) { vhdl->set_body (0,""); vhdl->set_comment(0,"---------------------------------------------------"); vhdl->set_comment(0," Ackitement"); vhdl->set_comment(0,"---------------------------------------------------"); vhdl->set_body (0,""); for (uint32_t i = 0; i < _param->_nb_port_read; i++) vhdl->set_body (0,"out_READ_"+toString(i)+"_ACK <= '1';"); for (uint32_t i = 0; i < _param->_nb_port_write; i++) vhdl->set_body (0,"out_WRITE_"+toString(i)+"_ACK <= '1';"); vhdl->set_body (0,""); vhdl->set_comment(0,"---------------------------------------------------"); vhdl->set_comment(0," Read RegisterFile"); vhdl->set_comment(0,"---------------------------------------------------"); vhdl->set_body (0,""); for (uint32_t i = 0; i < _param->_nb_port_read; i++) { std::string str_address; if (_param->_have_port_address) str_address = "conv_integer(in_READ_"+toString(i)+"_ADDRESS)"; else str_address = "0"; vhdl->set_body (0,"out_READ_"+toString(i)+"_DATA <= reg_DATA ("+str_address+ ") when in_READ_"+toString(i)+"_VAL = '1' else "+ std_logic_others(_param->_size_word,0)+";"); } vhdl->set_body (0,""); vhdl->set_comment(0,"---------------------------------------------------"); vhdl->set_comment(0," Write RegisterFile"); vhdl->set_comment(0,"---------------------------------------------------"); vhdl->set_body (0,""); vhdl->set_body (0,"RegisterFile_write: process (in_CLOCK)"); vhdl->set_body (0,"begin -- process RegisterFile_write"); vhdl->set_body (1,"if in_CLOCK'event and in_CLOCK = '1' then"); for (uint32_t i = 0; i < _param->_nb_port_write; i++) { std::string str_address; if (_param->_have_port_address) str_address = "conv_integer(in_WRITE_"+toString(i)+"_ADDRESS)"; else str_address = "0"; vhdl->set_body (2,"if (in_WRITE_"+toString(i)+"_VAL = '1') then"); vhdl->set_body (3,"reg_DATA("+str_address+") <= in_WRITE_"+toString(i)+"_DATA;"); vhdl->set_body (2,"end if;"); } vhdl->set_body (1,"end if;"); vhdl->set_body (0,"end process RegisterFile_write;"); }; \end{verbatim} \subsubSection{Fichier RegisterFile\_Monolithic.vhdl} \begin{verbatim} library ieee; use ieee.numeric_bit.all; use ieee.numeric_std.all; use ieee.std_logic_1164.all; use ieee.std_logic_arith.all; use ieee.std_logic_misc.all; --use ieee.std_logic_signed.all; use ieee.std_logic_unsigned.all; --use ieee.std_logic_textio.all; library work; use work.RegisterFile_Monolithic_Pack.all; entity RegisterFile_Monolithic is port ( in_CLOCK : in std_logic; in_NRESET : in std_logic; in_READ_0_VAL : in std_logic; out_READ_0_ACK : out std_logic; in_READ_0_ADDRESS : in std_logic_vector(8 downto 0); out_READ_0_DATA : out std_logic_vector(31 downto 0); in_WRITE_0_VAL : in std_logic; out_WRITE_0_ACK : out std_logic; in_WRITE_0_ADDRESS: in std_logic_vector(8 downto 0); in_WRITE_0_DATA : in std_logic_vector(31 downto 0) ); end RegisterFile_Monolithic; architecture behavioural of RegisterFile_Monolithic is type Tregfile is array (511 downto 0) of std_logic_vector(31 downto 0); signal reg_DATA : Tregfile; begin ----------------------------------------------------- -- Ackitement ----------------------------------------------------- out_READ_0_ACK <= '1'; out_WRITE_0_ACK <= '1'; ----------------------------------------------------- -- Read RegisterFile ----------------------------------------------------- out_READ_0_DATA <= reg_DATA (conv_integer(in_READ_0_ADDRESS)) when in_READ_0_VAL = '1' else (others => '0'); ----------------------------------------------------- -- Write RegisterFile ----------------------------------------------------- RegisterFile_write: process (in_CLOCK) begin -- process RegisterFile_write if in_CLOCK'event and in_CLOCK = '1' then if (in_WRITE_0_VAL = '1') then reg_DATA(conv_integer(in_WRITE_0_ADDRESS)) <= in_WRITE_0_DATA; end if; end if; end process RegisterFile_write; end behavioural; \end{verbatim} \Section {Test du VHDL}\label{testbench} Lors d'une simulation systemC, \cpu va générer des fichiers de tests pour le modèle VHDL de chaque composant interne. Ces tests sont tous écrit de la même façon. Soit l'interface X et le signal Y : \begin{itemize} \item Une variable {\it counter} indique le numéro de cycle courant. Elle est initialisé à 0. Elle est incrémenté à chaque cycle. \item Le composant est instancé avec des signaux internes de même nom. \item Pour chaque cycle, on affecte à chaque entrée la valeur obtenu lors de la simulation systemC. \item Pour chaque sortie, on définit un signal dont le nom est {\it out\_X\_Y\_test}. Ces signaux sont affecté au valeur obtenu des sorties lors la simulation systemC. \item Le signal {\it interface\_X\_test\_ok} est égal à 1 si chaque sortie calculé par la simulation VHDL est égal au sortie attendu (calculé par la simulation systemC). \item Le signal {\it interface\_X\_test\_transaction} est à 1 si tous les signaux de validité et d'aquittement sont à 1. \item Le signal {\it interface\_X\_test\_tmp} est à 1 si la simulation systemC indique qu'il y a une transaction et que la simulation vhdl indique également une transaction et que toute les sortie sont bien les sorties obtenu. Si la simulation systemC n'indique pas de transaction, on vérifie que c'est également le cas pour la simulation VHDL. \item Le signal {\it interface\_X\_test} est égal au signal {\it interface\_X\_test\_tmp} quand il n'y a pas de reset. sinon il est positionné à 1. \item Le signal {\it test} est un et logique entre tout tous les signaux {\it interface\_X\_test}. \item La simulation VHDL ce termine si le signal {\it test} vaut 0 (dans ce cas, le test est un échec), ou que le registre {\it counter} vaut le nombre de cycle requis par la simulation systemC (dans ce cas, le test est un succès). \end{itemize} Nous pouvons remarquer que : \begin{itemize} \item Nous vérifions la compatibilité au cycle des transactions entre le modèle systemC et le modèle VHDL. \item Nous vérifions la compatibilité au bit près des données échangé lors qu'il y a une transaction sur une interface. \item Nous ne vérifions pas la compatibilité au bit près des données présente sur une interface qui ne fait pas de transaction lors d'un cycle. \end{itemize}