[WikiItic] [TitleIndex] [WordIndex

Exemple VHDL: Alu

Aquest exemple treballa amb una ALU senzilla:

ghdl és un compilador lliure de VHDL que permet compilar i simular circuïts descrits amb VHDL. El resultat final d'usar aquest compilador és un programa executable que, quan l'executem, simula el circuit.

L'ALU

Primera aproximació

La ALU té aquesta descripció en VHDL:

   1 library ieee;
   2 use ieee.std_logic_1164.all;
   3 
   4 entity alu is
   5   port
   6     (A, B  : in  std_logic_vector (3 downto 0);
   7      opcode: in  std_logic_vector (3 downto 0);
   8      result: out std_logic_vector (3 downto 0) );
   9 end alu;
  10 
  11 architecture comportament of alu is
  12 begin
  13   process (opcode, A, B)
  14   begin
  15     case opcode is
  16       when "0000" => result <= not A;
  17       when "0001" => result <= not (A and B);
  18       when "0010" => result <= not A or not B;
  19       when "0011" => result <= "1111";
  20       when "0100" => result <= not A or not B;
  21       when "0101" => result <= not B;
  22       when "0110" => result <= not A xor not B;
  23       when "0111" => result <= A or not B;
  24       when "1000" => result <= not A and B;
  25       when "1001" => result <= A xor B;
  26       when "1010" => result <= B;
  27       when "1011" => result <= A or B;
  28       when "1100" => result <= "0000";
  29       when "1101" => result <= A and not B;
  30       when "1110" => result <= A and B;
  31       when "1111" => result <= A;
  32       when others => result <= "----";
  33     end case;
  34   end process;
  35 end comportament;

Noteu que cal una sentència «when others» ja que VHDL requereix que una sentència «case» sigui completa i abasti tot el domini. Com que les variables de tipus std_logic_vector no són binàries sinó que poden tenir altres valors com ara Z,X, etc., les combinacions possibles de quatre bits són moltes més de les que semblen. S'ha de dir que algunes eines poc respectuoses amb l'estàndart no obliguen a usar «when others».

Per comprovar que la sintaxi és correcta, simplement ens cal usar ghdl amb la opció -s fent:

   1    $ ghdl -s alu.vhdl

Si troba errors sintàctics es queixarà amb els corresponents missatges d'error similars a aquest:

   1   $ ghdl -s alu.vhdl
   2   alu.vhdl:10:47: extra ';' at end of interface list

Fixeu-vos que en aquest missatge es diu:

  1. Que l'error és al fitxer alu.vhdl

  2. Que és a la línia 10
  3. Que és a la columna 47 4: Que el problema és que manca un ';' al final d'una llista

Una vegada hem comprovat que la nostra descripció VHDL és sintàcticament correcta cal demanar al compilador que l'analitzi. A tal efecte usem la opció -a del compilador ghdl:

   1   $ ghdl -a alu.vhdl

El resultat, si tot va bé, serà un fitxer de nom alu.o.

Finalment, la darrera fase l'anomenem elaboració i es fa usant ghdl amb l'opció -e fent:

   1   $ ghdl -e alu

Fixeu-vos que ara només usem el nom de l'entitat (declarat al bloc entity com a alu) i no el nom del fitxer que la conté (alu.vhdl). El resultat és un programa executable anomenat simplement alu. El podeu executar fent simplement:

   1   $ ./alu

Naturalment no veureu res! La descripció de l'alu no té cap tipus de senyal d'entrada que la faci funcionar, per tant quan la simulem... simplement no fa res. Més endavant ja veurem com subministrar-li senyals d'entrada.

Petites millores

La descripció que tenim de l'ALU té un element empipador: el tipus de dades que descriu els busos es repeteix aquí i allí de forma sistemàtica. Seria interessant poder definir un nou tipus de dades «bus» que permetés simplificar la descripció.

A tal efecte, definim un nou package en un nou fitxer que anomenarem tipus.vhdl i que té el següent contingut:

   1    library ieee;
   2    use ieee.std_logic_1164.all;
   3    package tipus is
   4      subtype bus_4 is std_logic_vector (3 downto 0);
   5    end tipus;

Aqui es defineix el tipus «bus_4» com un vector de 4 bits. Ara podem usar aquest bus en la definició de l'ALU. Només cal fer els següents canvis:

   1   library ieee;
   2   library work;
   3   use ieee.std_logic_1164.all;
   4   use work.tipus.all;              
   5 
   6 
   7   entity alu is
   8     port
   9       (A, B, opcode : in  bus_4;
  10        result :       out bus_4 );
  11   end alu;
  12 
  13   architecture comportament of alu is
  14   begin
  15     process (opcode, A, B)
  16     begin
  17       case opcode is
  18         when "0000" => result <= not A;
  19         when "0001" => result <= not (A and B);
  20         when "0010" => result <= not A or not B;
  21         when "0011" => result <= "1111";
  22         when "0100" => result <= not A or not B;
  23         when "0101" => result <= not B;
  24         when "0110" => result <= not A xor not B;
  25         when "0111" => result <= A or not B;
  26         when "1000" => result <= not A and B;
  27         when "1001" => result <= A xor B;
  28         when "1010" => result <= B;
  29         when "1011" => result <= A or B;
  30         when "1100" => result <= "0000";
  31         when "1101" => result <= A and not B;
  32         when "1110" => result <= A and B;
  33         when "1111" => result <= A;
  34         when others => result <= "----";
  35       end case;
  36     end process;
  37   end comportament;

La capçalera d'aquest fitxer es pot simplificar atès que la llibreria work, que correspon amb els packages que tens en el mateix directori, no cal importar-la. Per tant la capçalera pot ser:

   1   library ieee;
   2   use ieee.std_logic_1164.all;
   3   use work.tipus.all;              

Per processar això, ara cal analitzar primer tipus.vhdl (no farem test sintàctic atès que suposarem que és correcte). Fem-ho així:

   1   $ ghdl -a tipus.vhdl
   2   $ ghdl -a alu.vhdl

Fixeu-vos que cal analitzar cada package per separat i cal respectar l'ordre natural: com que alu usa tipus, primer cal analitzar tipus i després alu. Una vegada feta la fase d'anàlisi, podem passar a la fase d'elaboració del simulador:

   1   $ ghdl -e alu

Fixeu-vos que només cal elaborar l'entitat que ens interessa simular, en aquest cas l'alu. El resultat, com abans, és un programa executable de nom alu que, de nou, si l'executeu no fa res en especial per que no s'han definit senyals d'entrada.

Comprovant el funcionament

Per comprovar que l'alu especificada es comporta com cal, podem definir un "test bench" que és quelcom similar al que en programació seria un "unit test". Això ho podem fer usant el mateix VDHL.

La idea és molt senzilla: només cal definir una nova entitat que és una composició de diverses components. En el nostre cas, serà la composició d'una sola component que és una alu. En la implementació d'aquesta entitat (architecture), generarem els senyals que volguem i els dirigirem a la alu comprovant després que el resultat és correcte.

Aquesta nova entitat l'anomenarem alu_tb1 i la definirem en el fitxer alu_tb1.vhdl de la següent forma:

   1 library ieee;
   2 use ieee.std_logic_1164.all;
   3 use work.tipus.all;                     -- inclou el subtipus bus_4
   4 
   5 entity alu_tb1 is
   6 end alu_tb1;
   7 
   8 architecture behavior of alu_tb1 is
   9 
  10   component alu
  11     port (
  12       A      : in  bus_4;
  13       B      : in  bus_4;
  14       opcode : in  bus_4;
  15       result : out bus_4);
  16   end component;
  17 
  18   signal T_A, T_B, T_opcode, T_result : bus_4;
  19   
  20 begin  -- behavior
  21 
  22   alu_UT : alu port map (
  23     A      => T_A,
  24     B      => T_B,
  25     opcode => T_opcode,
  26     result => T_result);
  27 
  28   process
  29     begin 
  30       T_A      <= "0000";
  31       T_B      <= "1001";
  32       T_opcode <= "1110";
  33       wait for 10 ns;
  34       assert (T_result = "0000") report "AND incorrect" severity failure;
  35 
  36       T_A      <= "0001";
  37       T_B      <= "1001";
  38       T_opcode <= "1110";
  39       wait for 10 ns;
  40       assert (T_result = "0001") report "AND incorrect" severity failure;
  41 
  42       wait;
  43     end process;
  44 
  45 end behavior;

Fixeu-vos en alguns detalls de com és la entitat de test. Només té arquitectura. Amb la sentència component definim quines components fa servir aquesta entitat. En aquest cas, només una alu. Definim també alguns signals, que juguen el paper de "cables" i usarem per fer el "cablejat" d'aquesta entitat.

A continuació instanciem una alu, que anomenem alu_UT, i connectem als signals que hem definit anteriorment. Finalment, una vegada definit el montatge per fer els tests, comencem a fer una seqüència de tests sota la sentència process. La idea és senzilla, injectem en cada cable el senyal que ens interessa, esperem un temps prudencial i comprovem mitçant una sentència assert que comprova el senyal de sortida.

Si processeu el test bench fent:

   1   $ ghdl -a alu_tb1
   2   $ ghdl -e alu_tb1

i obteniu un executable anomenat alu_tb1. Si l'executeu fent:

   1   $ ./alu_tb1

i no obteniu cap sortida és que l'alu està fent l'operació AND com esperaveu. Altrament les sentències assert haurien aturat el procés tot indicant l'error.

També podeu "espiar" què està passant amb els senyals dins del circuit. Només cal que, en fer la simulació, indiqueu que s'enregistrin els senyals. Ho podeu fer així:

   1    $ ./alu_tb1 --vcd=alu_tb1.vcd

Això genera un fitxer en format vcd que conté l'evolució dels senyals. Si els voleu veure en forma de cronograma només heu d'usar l'eina gtkwave (que potser haureu d'instal·lar abans usant aptitude) fent:

   1   $ gtkwave alu_tb1.vcd

L'aplicació té una interfície gràfica senzilla que permet explorar els senyals:

attachment:gtkwave-alu_tb1.png


2023-07-03 11:37