Descrição e Projeto de Circuitos Utilizando VHDL O projeto de circuitos digitais de forma automatizada se tornou prática industrial comum. As principais vantagens são : Portabilidade de tecnologia Melhor documentação do projeto As duas principais linguagens utilizadas são o Verilog HDL e o VHDL. O termo VHDL é um acrônimo das seguintes siglas : VHSIC + HDL, onde VHSIC VHDL = Very High Speed Integrated Circuits = Hardware Description Language A linguagem VHDL pode ser complexa, dependendo do que se quer descrever e/ou projetar. No entanto, utilizaremos um pequeno sub-conjunto de fácil aprendizado e suficiente para a síntese de circuitos digitais de modesta complexidade. Apresentamos aqui alguns construtores que serão utilizados : variable assignment (atribuição de variável) xpto := 50 signal assignment (atribuição de sinal) nextstate <= HIGHWAY_GREEN comparações lógicas = (equal), /= (not equal),> (greater than), < (less than)<= ( less than or equal), >= (greater than or equal) operadores lógicos (and, xor, or, nand, nor, xnor, not ) if statement if ( presentstate = CHECK_CAR ) then... end if elsif... for statement (loops) Outros exemplos de construtores : when else, case, wait.
VHDL POR EXEMPLOS Utilizaremos exemplos de circuitos combinacionais para introduzir o referido sub-conjunto do VHDL. Serão também mostradas várias implementações do mesmo projeto. Os exemplos serão : 1. 2 to 1 Mux 2. 8-level priority circuit 3. 3 to 8 Decoder MODELO BÁSICO entity model_name is port ( list of inputs and outputs ); end model_name; architecture architecture_name of model_name is... VHDL concurrent statements... end architecture_name ; A declaração " entity " define a interface do modelo com o meio externo. A lista " port " define os sinais externos. O bloco " architecture " especifica o aspecto funcional do modelo : seu nome é definido pelo projetista podem ser definidas múltiplas arquiteturas para a mesma entidade; determinadas configurações serão utilizadas para especificar que arquitetura deve ser utilizada para uma particular entidade (nossos exemplos utilizarão uma única arquitetura por entidade, que será sempre chamada " behavior ".
2 to 1 MUX Utilizando when else library IEEE; use IEEE.std_logic_1164.all; vhdl model for 2 to 1 mux, 8 bits wide entity mux2to1 is port ( signal s: in std_logic; signal zero,one: in std_logic_vector(7 downto 0); signal y: out std_logic_vector(7 downto 0) ); end mux2to1; architecture behavior of mux2to1 is y <= one when (s = 1 ) else zero; Standard Logic 1164 library IEEE; use IEEE.std_logic_1164.all; O comando " library " é utilizado para referenciar um grupo de units préprogramadas em VHDL ( packages ). O comando " use " especifica quais entidades ou packages serão utilizadas desta biblioteca. No caso acima, o comando use IEEE.std_logic_1164.all; importa todas as procedures/functions da package std_logic_1164 package, da biblioteca IEEE. A package std_logic_1164 define vários tipos de dados. Dentre eles podemos destacar : tipo bit : valores 1 e 0 (padrão no VHDL) Z ( alta impedância), (don t care) tipo single bit std_logic e vector std_logic (para barramentos).
2/1 MUX Entity entity mux2to1 is port ( signal s: in std_logic; signal zero,one: in std_logic_vector(7 downto 0); signal y: out std_logic_vector(7 downto 0) ); end mux2to1; A declaração " entity " define a interface do modelo com o meio externo. A lista " port " define os sinais externos. A definição dos sinais consiste em: name, mode, e type de cada um dos sinais. Por enquanto serão utilizados como mode apenas in, out ou inout. Os types serão : std_logic ( bit ) ou std_logic_vector (barramentos). A especificação matricial no tipo std_logic_vector define o " tamanho" do sinal: std_logic_vector (7 downto 0) (descending range) std_logic_vector (0 to 7) (ascending range) Ambos são sinais de 8 bits. Os termos descending/ascending range afetarão comandos como: y <= 11110000 ; Para descending range, y(7) é 1 ; para ascending range y(0) é 1.
2/1 MUX Architecture architecture behavior of mux2to1 is y <= one when (s = 1 ) else zero; O bloco " architecture " especifica o aspecto funcional do modelo. Seu nome é definido pelo projetista, porém, por facilidade o chamaremos sempre por " behavior ". O comando when... else é um comando condicional, com funcionamento idêntico aquele observado em outras linguagens.
2/1 MUX Architecture Utilizando Lógica Booleana architecture behavior of mux2to1 is signal temp: std_logic_vector(7 downto 0); temp <= (s, s, s, s, others => s); y <= (temp and one) or (not temp and zero); Operadores booleanos são utilizados para gerar a operação MUX. Um conjunto de atribuições deve ser feito e pode ter várias formas : temp <= (others => s); a palavra others dá o valor default temp <= (s, s, s, s, s, s, s, s) ; atribuição posicional ( 7 downto 0 ) temp <= (4=>s, 7=>s, 2=>s, 5=>s, 3=>s, 1=>s, 6=>s, 0=>s) atribuição nominal ou uma combinação dos anteriores. 2/1 MUX Architecture Utilizando um Processo architecture behavior of mux2to1_8 is comb: process (s, zero, one) y <= zero; if (s = 1 ) then y <= one; end if; end process comb; Um processo é utilizado para gerar a operação MUX. O bloco processo é considerado como um simples comando concorrente. São permitidos apenas comandos sequenciais dentro do bloco processo. Atribuições de sinais são consideradas como ocorrências sequenciais, já que uma atribuição precede uma anterior feita para o mesmo sinal. if... else, case, for... loop são comandos sequenciais. A lista de sinais colocada após o comando process ( s, zero, one no exemplo acima ) é chamada sensitivity list ; um evento em qualquer destes sinais causará uma reavaliação do bloco processo, durante a simulação do modelo.
8 level Priority Encoder Modelo VHDL para um codificador de prioridade IO Interface Declaration entity priority is port ( signal y1, y2, y3, y4, y5, y6, y7: in std_logic; signal vec: out std_logic_vector(2 downto 0) ); end priority; Architecture body architecture behavior of priority is process (y1,y2,y3,y4,y5,y6,y7) if (y7 = 1 ) then vec <= 111 ; elsif (y6 = 1 ) then vec <= 110 ; elsif (y5 = 1 ) then vec <= 101 ; elsif (y4 = 1 ) then vec <= 100 ; elsif (y3 = 1 ) then vec <= 011 ; elsif (y2 = 1 ) then vec <= 010 ; elsif (y1 = 1 ) then vec <= 001 ; else vec <= B 000 ; end if; end process;
Priority Encoder novamente... Num processo, a ordem de comandos sequenciais que afetam uma saída comum define a prioridade daquelas atribuições. Architecture body architecture behavior of priority is process (y1,y2,y3,y4,y5,y6,y7) vec <= 000 ; if (y1 = 1 ) then vec <= 001 ; end if; if (y2 = 1 ) then vec <= 010 ; end if; if (y3 = 1 ) then vec <= 011 ; end if; if (y4 = 1 ) then vec <= 100 ; end if; if (y5 = 1 ) then vec <= 101 ; end if; if (y6 = 1 ) then vec <= 110 ; end if; if (y7 = 1 ) then vec <= 111 ; end if; end process; Como y7 é testado por último, ele terá a mais alta prioridade.
Examplo 3 to 8 Decoder entity dec3to8 is port ( signal sel: in std_logic_vector(2 downto 0); seletor signal en: in std_logic; enable signal y: out std_logic_vector(7 downto 0) saídas ativas LOW ); end dec3to8; architecture behavior of dec3to8 is process (sel,en) y <= 11111111 ; if (en = 1 ) then case sel is when 000 => y(0) <= 0 ; when 001 => y(1) <= 0 ; when 010 => y(2) <= 0 ; when 011 => y(3) <= 0 ; when 100 => y(4) <= 0 ; when 101 => y(5) <= 0 ; when 110 => y(6) <= 0 ; when 111 => y(7) <= 0 ; end case; end if; end process; Ao utilizar processos, um erro comum é esquecer de atribuir a uma saída um valor default. Todas as saídas devem ter valores defaults. Examplo: No arquivo dec3to8.vhd se não fosse atribuído a y o valor default de B 11111111, caso en is 0, então nenhum valor seria atribuído a y!
Alternativa para o 3 to 8 Decoder vhdl model for the 3 to 8 decoder utiliza atribuições condicionais signal, que são comandos concorrentes entity dec3to8_alt is port ( signal sel: in std_logic_vector(2 downto 0); seletor signal en: in std_logic; enable signal y: out std_logic_vector(7 downto 0) saídas ativas LOW ); end dec3to8_alt; architecture behavior of dec3to8_alt is y(0) <= 0 when (en = 1 and sel = 000 ) else 1 ; y(1) <= 0 when (en = 1 and sel = 001 ) else 1 ; y(2) <= 0 when (en = 1 and sel = 010 ) else 1 ; y(3) <= 0 when (en = 1 and sel = 011 ) else 1 ; y(4) <= 0 when (en = 1 and sel = 100 ) else 1 ; y(5) <= 0 when (en = 1 and sel = 101 ) else 1 ; y(6) <= 0 when (en = 1 and sel = 110 ) else 1 ; y(7) <= 0 when (en = 1 and sel = 111 ) else 1 ;
ENTITY testbench IS END; ------------------------------------------------------------------------ -- testbench for 8-bit adder -- reads file "vectors" ------------------------------------------------------------------------ LIBRARY ieee; USE ieee.std_logic_1164.all; USE std.textio.all; ARCHITECTURE adder8 OF testbench IS ----------------------------------- -- component declaration for addern ----------------------------------- COMPONENT addern GENERIC(n : integer); PORT (a : IN std_logic_vector(n DOWNTO 1); b : IN std_logic_vector(n DOWNTO 1); cin : IN std_logic; sum : OUT std_logic_vector(n DOWNTO 1); cout : OUT std_logic); END COMPONENT; -- declare one large signal SIGNAL ports : std_logic_vector(26 DOWNTO 1) := (OTHERS => 'Z'); -- declare an alias for each port -- this makes it easier to connect the signals to the component ALIAS a : std_logic_vector(8 DOWNTO 1) IS ports(26 DOWNTO 19); ALIAS b : std_logic_vector(8 DOWNTO 1) IS ports(18 DOWNTO 11); ALIAS cin : std_logic IS ports(10); ALIAS sum : std_logic_vector(8 DOWNTO 1) IS ports(9 DOWNTO 2); ALIAS cout : std_logic IS ports(1); BEGIN -- instantiate the component uut: addern GENERIC MAP(8) PORT MAP(a => a, b => b, cin => cin, sum => sum, cout => cout); -- provide stimulus and check the result test: PROCESS FILE vector_file : text IS IN "vectors"; VARIABLE l : line; VARIABLE vector_time : time; VARIABLE r : real; VARIABLE good_number : boolean; VARIABLE signo : integer; BEGIN WHILE NOT endfile(vector_file) LOOP readline(vector_file, l); -- read the time from the ning of the line
-- skip the line if it doesn't start with a number read(l, r, good => good_number); NEXT WHEN NOT good_number; vector_time := r * 1 ns; IF (now < vector_time) THEN WAIT FOR vector_time - now; END IF; -- convert real number to time -- wait until the vector time signo := 26; FOR i IN l'range LOOP CASE l(i) IS WHEN '0' => -- Drive 0 ports(signo) <= '0'; WHEN '1' => -- Drive 1 ports(signo) <= '1'; WHEN 'h' 'H' => -- Test for 1 ASSERT ports(signo) = '1'; WHEN 'l' 'L' => -- Test for 0 ASSERT ports(signo) = '0'; WHEN 'x' 'X' => -- Don't care NULL; WHEN ' ' ht => -- Skip white space NEXT; WHEN OTHERS => -- Illegal character ASSERT false REPORT "Illegal character in vector file: " & l(i); EXIT; END CASE; signo := signo - 1; END LOOP; END LOOP; ASSERT false REPORT "Test complete"; WAIT; END PROCESS; END; # Test vectors for 8-bit adder # 0 means force 0 # 1 means force 1 # L means expect 0 # H means expect 1 # X means don't care # # a b sum #time 87654321 87654321 cin 87654321 cout 0 11111111 00000000 0 XXXXXXXX X 100 00000001 00000001 1 HHHHHHHH L 200 00000010 00000001 0 LLLLLLHH L 300 10000000 00000001 0 LLLLLLHH L 400 11110000 00001111 0 HLLLLLLH L 500 00001111 11110000 1 HHHHHHHH L 600 10101010 10101010 1 LLLLLLLL H 700 00000000 00000000 0 LHLHLHLH H 800 00000000 00000000 0 LLLLLLLL L Arquivo VECTORS