ALU

  • Jako semestrální práce z předmětu PNIO, vznikl následující zdrojový kód v jazyce VHDL. Je v něm popsána jednoduchá strukura aritmeticko logické jednotky. Tabulka jejích funkcí je uvedena níže. Program byl překládán v Leapfrogu na systému Solaris.

ALU pracuje na principu klasických procesorových ALU, kdy pro provedení operace je potřeba jednak kód vstupního operandu a dále jeden až tři vstupní proměnné. Vstupy do ALU jsou zde max. dvě 8-mi bitová slova (A a B) a Carry (Cin). (Zdrojový kód lze snadno modifikovat pro ALU téměř libovolné šířky slova, pouhou změnou hodnoty proměnné N.)

TABULKA FUNKCÍ
Operand (OP)   Logická/Aritmetická operace (Z)
"0000"Z'range => Cin - na výstupní slovo je N krát Cin
"0001"A and B
"0010"A or B
"0011"not A
"0100"not B
"0101"A xor B
"0110"A nand B
"0111"A nor B
"1000"A - B + Cin
"1001"A + B + Cin
"1010"A + A + Cin
"1011"A + Cin
"1100"A - B - Cin
"1101"A + B - Cin
"1110"A + A - Cin
"1111"A - Cin

Tabulka základních funkcí


KOMENTOVANÝ ZDROJOVÝ KÓD
ALU - definice použitých knihoven a jejich podčástí.

-- ALU



library IEEE;

use IEEE.std_logic_1164.all;

use IEEE.std_logic_arith.all;


Definice entity ALU a vstupních (A, B - vstupní data, OP - operace, Cin - Carry in) a výstupních (Z - výstup, Cout - Carry out, OVF - overflow) signálů. N je pouze konstanta určující délku slova.

entity ALU is

   generic ( N : in integer := 8);

   port (

      A, B	: in std_logic_vector(N-1 downto 0);

      OP	: in std_logic_vector(3 downto 0);

      Cin	: in std_logic;

      Cout, OVF	: out std_logic;

      Z		: out std_logic_vector(N-1 downto 0));

end ALU;


Definice vlastní struktury (architektury) ALU.
Subtype unsigned je dodefinován jako std_logic_vector, pouze pro přehlednost ve zdrojovém kódu.
Následuje definice, zde jediného, jakoby paralelního procesu. Proměnné variable slouží pouze pro potřeby uchovávání vnitřních stavů VHDL, nejedná se tedy o signály, které se implementují na čip.

architecture RTL of ALU is



--   signal A, B	: std_logic_vector(N-1 downto 0);

   

subtype unsigned is std_logic_vector;



begin



proc : process (A, B, Cin, OP)

   variable temp1, temp2, result : unsigned(N downto 0);

begin

   Z	<= (Z'range => '0');

   Cout	<= '0';

   OVF <= '0';

   result := (result'range => '0');

   temp1  := (temp1'range => '0');

   temp2  := (temp2'range => '0');

 -- zakladni operace

 -- logicke operace nebude carry ovlivnovat

   case OP is

	when "0000" => Z <= (Z'range => Cin);

	when "0001" => Z <= A and B;

	when "0010" => Z <= A or B;

	when "0011" => Z <= not A;

	when "0100" => Z <= not B;

	when "0101" => Z <= A xor B;

	when "0110" => Z <= A nand B;

	when "0111" => Z <= A nor B;

	when others => null;

   end case;


Aritmetické operace byly zapsány s ohledem na minimalizaci množství implementovaných hradel na výsledném čipu. (Minimalizace je pouze mnou odhadovaná, syntéza čipu nebyla testována.)
Operace byly rozděleny do čtyř skupin, kde první dvě jsou plnou implementací funkce odečítání (zde A-B+Cin a A-B-Cin), třetí skupina je sčítání A+X+Cin a čtvrtá je A+X-Cin. Možnosti takto rozdělit funkce bylo dosaženo vhodnou optimalizací volby operačních kódů instrukcí.
Aritmetické přeplnění se indikuje tehdy, je-li výsledek součtu dvou záporných čísel číslo kladné, nebo je-li součet dvou kladných čísel záporný. V tom případě bude indikátor OVF=1. (Doplňkový kód) Odvození fce OVF.
Cout je přetečení do N+1 bitu.

 -- ALU pouziva doplnkovy kod

   temp1 := '0' & unsigned(A);

   --plne operandy musi byt prvni

   if OP = "1000" then -- => Z <= A-B+Cin;

     temp2 := '0' & unsigned(B);

     result := temp1 - temp2 + Cin;

     -- OVF <= ((not(temp1(N-1)) and not(temp2(N-1))) and result(N-1))

     --        or ((temp1(N-1) and temp2(N-1)) and not(result(N-1));

     OVF <= (temp1(N-1) xor temp2(N-1))

            and (temp1(N-1) xor result(N-1));

     Z <= result((N-1) downto 0);

   elsif OP = "1100" then -- => Z <= A-B-Cin;

     temp2 := '0' & unsigned(B);

     result := temp1 - temp2 - Cin;

     -- OVF <= ((not(temp1(N-1)) and not(temp2(N-1))) and result(N-1))

     --        or ((temp1(N-1) and temp2(N-1)) and not(result(N-1));

     OVF <= (temp1(N-1) xor temp2(N-1))

            and (temp1(N-1) xor result(N-1));

     Z <= result((N-1) downto 0);   

   elsif OP(3 downto 2) = "10" then -- + Cin 

     case OP(1 downto 0) is -- scitani

	when "01" => -- Z <= A+B+Cin;

	   temp2 := '0' & unsigned(B);

	when "10" => -- Z <= A+A+Cin;

	   temp2 := '0' & unsigned(A);

	when "11" => -- Z <= A+Cin;

	   temp2 := (temp2'range => '0');

	when others => null;

     end case;

     result := temp1 + temp2 + Cin;

     -- OVF <= ((not(temp1(N-1)) and not(temp2(N-1))) and result(N-1))

     --        or ((temp1(N-1) and temp2(N-1)) and not(result(N-1));

     OVF <= ((not (temp1(N-1) xor temp2(N-1)))

            and (temp1(N-1) xor result(N-1)));

     Z <= result((N-1) downto 0);

   elsif OP(3 downto 2) = "11" then -- -Cin

     case OP(1 downto 0) is

	when "01" => -- Z <= A+B-Cin;

	   temp2 := '0' & unsigned(B);

	when "10" => -- Z <= A+A-Cin;

	   temp2 := '0' & unsigned(A);

	when "11" => -- Z <= A-Cin;

	   temp2 := (temp2'range => '0');

	when others => null;

     end case;

     result := temp1 + temp2 - Cin;

     -- OVF <= ((not(temp1(N-1)) and not(temp2(N-1))) and result(N-1))

     --        or ((temp1(N-1) and temp2(N-1)) and not(result(N-1));

     OVF <= ((not (temp1(N-1) xor temp2(N-1)))

           and (temp1(N-1) xor result(N-1)));

     Z <= result((N-1) downto 0);

   end if;



   Cout <= result(N);



end process;

end RTL;


Velmi podstatná část kódu, sloužící pro testování správné funkce ALU.

-- testovani ALU



library IEEE;

use IEEE.std_logic_1164.all;

use IEEE.std_logic_arith.all;



entity TestALU_B is

end TestALU_B;



architecture BEH of TestALU_B is



   component ALU

   generic ( N : in integer := 8);

   port (

	A, B	: in std_logic_vector(N-1 downto 0);

	OP	: in std_logic_vector(3 downto 0);

	Cin	: in std_logic;

	Cout, OVF	: out std_logic;

	Z	: out std_logic_vector(N-1 downto 0)

	);

   end component;

   constant PERIOD	: time := 20 ns;

   constant N		: integer := 8;

   signal A		: std_logic_vector((N - 1) downto 0);

   signal B		: std_logic_vector((N - 1) downto 0);

   signal S		: std_logic_vector(3 downto 0);

   signal Cin		: std_logic;

   signal Cout		: std_logic;

   signal OVF		: std_logic;

   signal Z		: std_logic_vector((N - 1) downto 0);

begin



   ALU0 : ALU

	generic map (N => N)

	port map (A => A, B => B, OP => S, Cin => Cin, Cout => Cout,

	OVF => OVF, Z => Z);



   process

	variable Aint : integer range 0 to 255 := 3;

	variable Bint : integer range 0 to 255 := 230;

   begin

	for i in 0 to 1 loop

	   Aint := (Aint + 5) mod 255;

	   Bint := (Bint + 53) mod 255;

	   A <= to_stdlogicvector(Aint, N);

	   B <= to_stdlogicvector(Bint, N);

	   if (i = 0) then

		Cin <= '0';

		for k in 0 to 15 loop

		   S <= to_stdlogicvector(k, 4);

		   wait for PERIOD;

		end loop;

	   else

		Cin <= '1';

		for k in 0 to 15 loop

		   S <= to_stdlogicvector(k, 4);

		   wait for PERIOD;

		end loop;

	   end if;

	end loop;

	wait for 2 * PERIOD;

   end process;

end BEH;


Spuštění vlastního testu.
Pokud byste měli ve zdrojovém kódu více struktur ALUX, které byste chtěli otestovat, dopíší se zde.

library IEEE;

use IEEE.std_logic_1164.all;

use IEEE.std_logic_arith.all;



configuration BEH of TestALU_B is

   for BEH

	for ALU0 : ALU

	   use entity work.ALU(RTL);

	end for;

   end for;

end BEH;




To je tedy celý, komentovaný zdrojový kód, jeho čistou podobu si můžete stáhnout na http://cs.felk.cvut.cz/~xpribyla/alu/.