ELD129003-Engtelecom (Diário) - Prof. Marcos Moecke
1 Registro on-line das aulas
1.1 Unidade 1 - Aula inicial, Introdução a disciplina
- 1 ENCONTRO
Unidade 1 - Aula inicial, Introdução a disciplina |
---|
|
1.2 Unidade REV - PRIMEIRO CONTATO COM VHDL
- 4 ENCONTROS
Unidade REV - PRIMEIRO CONTATO COM VHDL | ||
---|---|---|
library library_name;
use library_name.package_name.all;
entity entity_name is
[generic (
cons_name1: const_type const_value;
cons_name2: const_type const_value;
...
cons_nameN: const_type const_value);]
[port (
signal_name1: mode signal_type;
signal_name2: mode signal_type;
...
signal_nameN: mode signal_type);]
[declarative_part]
[begin
statement_part]
end [entity] [entity_name];
architecture arch_name of entity_name is
[declarative_part]
begin
statement_part
end [architecture] [arch_name];
1.2.1 REVISÃO ATUAL
Para implementar circuitos aritméticos, ao invés de se descrever o circuito com portas lógicas conforme mostrado para somadores, subtratores, comparadores e multiplicadores, deve-se utilizar os operadores aritméticos, e o compilador realizará a escolha do melhor circuito para cada caso. Inicialmente apresentamos alguns exemplos utilizando dados do tipo integer. Para o uso do tipo integer, se não houver limitação da faixa de valores, o compilador entenderá que os sinais devem ter 32 bits, o que gera circuitos muito maiores que normalmente necessário. Assim, ao usar as entradas e saidas como integer sem range, o diagrama RTL mostrará que o circuito foi construido com 32 bits [31..0]. Nos dispositivos da familia Cyclone IV E serão utilizados 32 elementos lógicos para tal circuito. entity somador is
port (
a, b : in integer;
s : out integer;
end entity;
architecture ifsc of somador is
begin
s <= a + b;
end architecture;
Figura 4.1 - Código RTL do somador com tipo integer sem range ![]() Figura 4.2 - Technology Map do somador com tipo integer sem range ![]() Por isso, o uso correto do tipo integer, exige que se limite a faixa de valores (range 0 to 15), o que fará com que o compilador atribua para os sinais a quantidade correta de bits, gerando circuitos de tamanho adequado. Assim, ao usar as entradas e saidas como integer com range 0 to 15, o diagrama RTL mostrará que o circuito foi construido com 4 bits [3..0]. Nos dispositivos da familia Cyclone IV E serão utilizados 4 elementos lógicos para tal circuito. entity somador is
port (
a, b : in integer range 0 to 15;
s : out integer range 0 to 15);
end entity;
architecture ifsc of somador is
begin
s <= a + b;
end architecture;
Figura 4.3 - Código RTL do somador com tipo integer com range ![]() Figura 4.4 - Technology Map do somador com tipo integer com range ![]() Para fazer uma subtração, basta trocar o operador "+" pelo "-", e o compilador irá implementar um subtrator realizando o complemento 2 da entrada b. entity subtrator is
port (
a, b : in integer range 0 to 15;
s : out integer range 0 to 15);
end entity;
architecture ifsc of subtrator is
begin
s <= a - b;
end architecture;
Figura 4.5 - Código RTL do subtrator com tipo integer com range ![]() Note nesta figura que as entradas b[3..0] são conectadas ao B[4..1] do somador, e que o B[0] é conectado ao Vcc ("1"). O mesmo ocorre com a entrada A. Ao mesmo tempo a entrada b é invertida, gerando assim o complemento de dois dessa entrada. Assim para realizar uma subtração pode ser utilizado o mesmo circuito do somador. Para fazer um incrementador, um dos fatores do somador é substituído por '1' e o compilador irá implementar um circuito incrementador. entity incrementador is
port (
a : in integer range 0 to 15;
inc : out integer range 0 to 15);
end entity;
architecture ifsc of incrementador is
begin
inc <= a + 1;
end architecture;
Figura 4.6 - Código RTL do incrementador com tipo integer com range ![]() Note que no incrementador apenas a segunda entrada do módulo RTL somador passa a ter um valor fixo '1'. Isso faz com que o hardware necessário para efetuar a soma (+1) é reduzido.
entity decrementador is
port (
a : in integer range 0 to 15;
dec : out integer range 0 to 15);
end entity;
architecture ifsc of decrementador is
begin
dec <= a - 1;
end architecture;
Figura 4.7 - Código RTL do decrementador com tipo integer com range ![]() Note que no decrementador apenas a segunda entrada do módulo RTL somador passa a ter um valor fixo, mas também nas duas entradas o sinal A(0) e B(0) recebem o valor fixo '1', para produzir um carry_in para a soma do bit A(1) com B(1). Para fazer uma multiplicação, basta usar o operador "*" e o compilador irá implementar um multiplicador. Neste caso para evitar o overflow é importante definir o range da saída com um tamanho suficiente para comportar o maior produto. entity multiplicador is
port (
a, b : in integer range 0 to 15;
s : out integer range 0 to 15*15);
end entity;
architecture ifsc of multiplicador is
begin
s <= a * b;
end architecture;
Figura 4.6 - Código RTL do multiplicador com tipo integer com range ![]() Note que esse circuito no Cyclone IV E necessita de 31 elementos lógicos, e no caso em que multiplicador tem entradas com 4 bits [3..0], a saída terá 8 bits [7..0]. Caso a saída não tenha a quantidade suficiente de bits, haverá overflow e a resultado poderá estar incorreto. Para fazer uma divisão, basta usar o operador "/" e o compilador irá implementar um divisor inteiro. O tamanho do quociente deve ser igual ao dividendo. entity divisor is
port (
dividendo : in integer range 0 to 15;
divisor : in integer range 0 to 3;
quociente : out integer range 0 to 15;
resto : out integer range 0 to 3
);
end entity;
architecture ifsc of divisor is
begin
quociente <= dividendo / divisor;
resto <= dividendo rem divisor;
end architecture;
Figura 4.8 - Código RTL do divisor com tipo integer com range ![]() Multiplicações e divisões por potências de 2 (2, 4, 8, 16, ... $2^N$) não necessitam de nenhum elemento lógico pois podem ser implementados pelo simples deslocamento dos signais. Figura 4.8 - Código RTL do multiplicador por 4 ![]() Figura 4.9 - Código RTL do divisor por 2 ![]() ATENÇÃO: Multiplicações por constantes não utilizam os multiplicadores, pois são implementadas através de simples deslocamentos de sinais e somas. Assim multiplicar por 10 corresponde a multiplicar por 2 somar com a multiplicação por 8. Figura 4.10 - Código RTL do multiplicador por 10 ![]() O tipo INTEGER não é muito adequado para realizar as operações aritméticas por três motivos:
Figura 4.11 - Simulação com parada devido a erro de overflow no ModelSim ![]() Note que nessa simulação, ao atingir a soma 9 + 7 = 16, ocorre o overflow e o simulador indica o Fatal error e para. ** Fatal: (vsim-3421) Value 16 is out of range 0 to 15. Figura 4.12 - Erro Fatal no ModelSim ![]() Veja como ficaria um somador usando o tipo UNSIGNED como portas de entrada e saída. library ieee;
use ieee.numeric_std.all;
entity somador is
generic (N : natural := 4);
port (
a, b : in unsigned(N-1 downto 0);
s : out unsigned(4 downto 0)
);
end entity;
architecture ifsc of somador is
s <= a + b;
end architecture;
Mesmo essa solução acima não é a mais adequada, pois utiliza portas que não estão de acordo com a recomendação do padrão industrial e que é compatível com as ferramentas de EDA no mercado. O ideal é ter sempre as portas definidas com tipos std_logic ou std_logic_vector. Por isso a recomendação é adotar uma solução como a seguinte: library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;
entity somador_slv is
generic (N : natural := 4);
port (
a, b : in std_logic_vector(N-1 downto 0);
s : out std_logic(4 downto 0);
);
end entity;
architecture ifsc_v1 of somador_slv is
begin
s <= std_logic_vector(unsigned(a) + unsigned(b));
end architecture;
architecture ifsc_v2 of somador_slv is
signal a_uns, b_uns : unsigned(N-1 downto 0);
signal s_uns : unsigned(N-1 downto 0);
begin
s <= std_logic_vector(s_uns)
s_uns <= a_uns + b_uns;
a_uns <= unsigned(a);
a_uns <= unsigned(b);
end architecture;
Figura 4.12 - Simulação correta no ModelSim ![]() Note que nessa simulação, ao atingir a soma 9 + 7 = 16, ocorre o overflow, como são apenas 4 bits que são utilizados, o resultado 10000 é guardado como 0000 e e o simulador indica corretamente o s = 0 no instante 9ns. Para utilizar corretamente os tipos UNSIGNED e SIGNED leia atentamente
package NUMERIC_STD is
type UNSIGNED is array (NATURAL range <>) of STD_LOGIC;
type SIGNED is array (NATURAL range <>) of STD_LOGIC;
A biblioteca Numeric std.vhd ainda define os operadores (abs, "+", "-", "*", "/", rem, mod, sll, slr, ror, rol), comparações ("=", '/=', ">", ">=", "<", "<=") e operadores lógicos (not, and, nand, or, nor, xor, xnor) para os tipos SIGNED e UNSIGNED. Além disso também define algumas funções muito utilizadas como: --============================================================================
-- RESIZE Functions
--============================================================================
function RESIZE (ARG: SIGNED; NEW_SIZE: NATURAL) return SIGNED;
function RESIZE (ARG: UNSIGNED; NEW_SIZE: NATURAL) return UNSIGNED;
--============================================================================
-- Conversion Functions
--============================================================================
function TO_INTEGER (ARG: UNSIGNED) return NATURAL;
function TO_INTEGER (ARG: SIGNED) return INTEGER;
function TO_UNSIGNED (ARG, SIZE: NATURAL) return UNSIGNED;
function TO_SIGNED (ARG: INTEGER; SIZE: NATURAL) return SIGNED;
FONTE: http://www.doulos.com/knowhow/vhdl_designers_guide/numeric_std/ Ler e guardar a página sobre Aritmética com vetores em VDHL |
1.3 Unidade 2 - Dispositivos Lógicos Programáveis
- 3 ENCONTROS
Unidade 2 - Dispositivos Lógicos Programáveis |
---|
-- Exemplo: Declaração do circuito combinacional
-- Y = A'.B + A.B'
-- Autor: prof. Marcos Moecke
-- Data: 20/03/2025
-- Filename: REV1.vhd
-- A declaracao abaixo nao e necessaria pois o pacote standart da biblioteca std e autodeclarada (por default)
library std;
use std.standard.all;
--
entity REV1 is
port (a, B: in bit; y: out bit);
end entity;
-- Implementacao direta com funcoes logicas
architecture ifsc_v1 of rev1 is
begin
Y <= (not a and b) or (A and not B);
end architecture;
-- Implementacao com funcoes logica por etapas
architecture ifsc_v2 of rev1 is
signal nota, notb : bit; -- sinais internos podem ser declarados em uma unica linha se forem do mesmo tipo
signal and_1 : bit; -- o nome dos sinais nao pode usar palavras reservadas do VHDL como and.
signal and_2 : bit; -- usar uma linha para cada sinal pode ser uma boa pratica de documentacao
signal or_3 : bit;
begin
notA <= not A;
notb <= not B;
and_1 <= nota and b;
and_2 <= (A and notB);
or_3 <= and_1 or and_2;
Y <= or_3; -- assim se faz a conexao entre um sinal interno uma saida
-- as ultimas 2 linhas acima poderiam ser tambem simplificadas
-- Y <= and_1 or and_2;
end architecture;
-- Implementacao com WHEN-ELSE
architecture ifsc_v3 of rev1 is
begin
end architecture;
-- Implementacao com WITH-SELECT
architecture ifsc_v4 of rev1 is
begin
end architecture;
-- associacao da architetura a entidade.
-- Se nao for feita, a ultima arquitetura e associada
configuration rev1_cfg of REV1 is
-- for ifsc_v1 end for;
for ifsc_v2 end for;
-- for ifsc_v3 end for;
-- for ifsc_v4 end for;
end configuration;
Figura 2.17 - Altera - Visão geral do dispositivo Arria V SX e ST ![]() Figura 2.18 - Altera - Agilex 7 SoCs HPS Digrama de Blocos ![]()
|
1.4 Unidade 3 - Circuitos sequenciais (Implementação com HDL)
- 8 ENCONTROS
Unidade 3 - Circuitos sequenciais (Implementação com HDL) | ||||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
[rótulo:] PROCESS [(lista_de_sensibilidade)] [IS]
[parte_declarativa]
BEGIN
afirmação_sequencial;
afirmação_sequencial;
...
END PROCESS [rótulo];
[rótulo:] IF condição THEN
afirmação_sequencial;
afirmação_sequencial;
...
ELSIF condição THEN
afirmação_sequencial;
afirmação_sequencial;
...
ELSE
afirmação_sequencial;
afirmação_sequencial;
...
END IF [rótulo];
--Flip Flop tipo D com reset assincrono, sensivel a borda de subida.
process (clock,reset)
begin
if (reset = '1') then
q <= '0';
-- elsif (clock'event and clock = '1') then or
elsif (rising_edge(clock)) then
q <= d;
end if;
end process;
Figura 3.1 - RTL de Flip-flop D de borda de subida, com reset assíncrono ![]() --Flip Flop tipo D com preset assincrono e sinal de enable, sensivel a borda de descida.
process (clock, preset)
begin
if (preset = '1') then
q <= '1';
elsif (falling_edge(clock)) then
if (enable = '1') then
q <= d;
end if;
end if;
end process;
Figura 3.2 - RTL de Flip-flop D de borda de descida, com preset assíncrono e enable ![]()
--Latch tipo D com reset assincrono.
process (enable, reset, d)
begin
if (reset = '1') then
q <= '0';
elsif (enable='1')) then
q <= d;
end if;
end process;
Figura 3.3 - RTL de Latch D de com reset assíncrono e enable ativo alto ![]()
Figura 3.4 - Comparação do Technology Map de um Latch_D (esquerda) com FF_D (direita) ![]()
-- Flip Flop tipo D com reset síncrono sensível a borda de subida.
-- Modifique a descrição para que o reset_ass seja assíncrono e reset_sinc seja síncrono.
-- Note que a função rising_edge(clock) é equivalente a (clock'event and clock'last_value = '0' and clock = '1'))
process (clock, reset)
begin
if (reset = '1') then
q <= '0';
elsif (clock'event and clock'last_value = '0' and clock = '1')) then
q <= d;
end if;
end process;
Figura 3.5 - RTL do Flip-flop D com reset assíncrono e reset síncrono ![]()
Figura 3.6 - RTL do Registrador (de 4 bits) com reset assíncrono ![]() Figura 3.7 - Techonogy Map do Registrador (de 4 bits) com reset assíncrono ![]()
vlib rtl_work
vmap work rtl_work
vcom -93 -work work {../../FFD_N.vhd}
vsim work.ffd_n(ifsc_v1)
add wave -r sim:/ffd_n/*
force -freeze sim:/ffd_n/<nome do estimulo> valor1 tempo1, valor2 tempo2, valor3 tempo3
force -freeze sim:/ffd_n/clock v1 t1, v2 t2 -r Tclk
force -freeze sim:/ffd_n/clock 1 0, 0 50 -r 100
run XXX ps ou run XXX ns|us|ms|sec|min|hr
wave zoom full
=========== Files Selected: =========== /home/moecke/ELD2/2025.1/AULA7/FFD_N.qpf /home/moecke/ELD2/2025.1/AULA7/FFD_N.qsf /home/moecke/ELD2/2025.1/AULA7/FFD_N.vhd /home/moecke/ELD2/2025.1/AULA7/simulation/modelsim/tb_FFD_v2_melhor.do /home/moecke/ELD2/2025.1/AULA7/simulation/modelsim/tb_FFD_v_ruim.do /opt/intelFPGA/20.1/quartus/linux64/assignment_defaults.qdf ======= Total: 6 files to archive =======
Tipo de Circuito Características
Um dos aspectos de projeto mais difíceis de um circuito sequencial é satisfazer as restrições (constraints) de tempo (ex.: Tsetup e Thold). No MÉTODO SÍNCRONO, todos os DFFs utilizam em um único clock. Assim apenas é necessário lidar com a restrição de tempo de um elemento de memória.
Figura 5.21 - Diagrama conceitual de circuitos síncronos ![]()
No exemplo do contador binário livre com pulso de máximo é possível perceber um dos tipos de erros que o uso de código em um segmento pode gerar. Como cada atribuição feita a um sinal dentro do if rising_edge(clk) then, gera um flip-flop para a saida max_pulse, e pode causar atrasos indesejados em sinais. architecture extra_ff_max_pulse of binary_counter4_pulse is
signal r_reg: unsigned(3 downto 0);
begin
process(clk,reset)
begin
if (reset='1') then
r_reg <= (others=>'0');
elsif rising_edge(clk) then
r_reg <= r_reg + 1;
if r_reg="1111" then
max_pulse <= '1';
else
max_pulse <= '0';
end if;
end if;
end process;
q <= std_logic_vector(r_reg);
end architecture;
Figura 5.22 - Diagrama RTL contador com max_pulse output ![]() Figura 5.22 - Diagrama RTL contador com extra flip-flop in max_pulse output ![]() Figura 5.23 - Simulação do extra flip-flop in max_pulse output - errado ![]() Figura 5.24 - Simulação do max_pulse output - correto ![]() No exemplo do contador decimal (mod-10) é possível perceber um outro tipo de erros que o uso de código em um segmento pode gerar. Como um sinal (signal) é atualizado apenas no final do processo, isso pode levar a códigos com erro como mostrado no exemplo a seguir. architecture one_seg_arch_signal of mod10_counter is
constant TEN: integer := 10;
signal r_reg: unsigned(3 downto 0);
begin
-- register
process(clk,reset)
begin
if (reset='1') then
r_reg <= (others => '0');
elsif rising_edge(clk) then
r_reg <= r_reg + 1;
if r_reg=TEN then
r_reg <= (others => '0');
end if;
end if;
end process;
-- output logic
q <= std_logic_vector(r_reg);
end architecture;
Na comparação r_reg=TEN, espera-se que o r_reg tenha sido incrementado (r_reg <= r_reg + 1), mas o valor de r_reg ainda é o valor que tinha ao entrar no process. Assim o contador acaba contando de 0 a 10 e não de 0 a 9. architecture one_seg_arch_variable of mod10_counter is
constant TEN: integer := 10;
begin
-- register
process(clk,reset)
variable r_reg: unsigned(3 downto 0);
begin
if (reset='1') then
r_reg := (others => '0');
elsif rising_edge(clk) then
r_reg := r_reg + 1;
if r_reg = TEN then
r_reg := (others => '0');
end if;
end if;
-- output logic
q <= std_logic_vector(r_reg);
end process;
end architecture;
A solução acima utiliza variable no lugar de signal para o r_reg. Neste caso a comparação r_reg = TEN irá comparar corretamente o valor atual de r_reg já incrementado (r_reg := r_reg + 1), e a contagem será correta de 0 a 9.
-- state register
process(clock,reset)
begin
if (reset='1') then
centena_reg <= (others=>'0');
dezena_reg <= (others=>'0');
unidade_reg <= (others=>'0');
elsif rising_edge(clock) then
centena_reg <= centena_next;
dezena_reg <= dezena_next;
unidade_reg <= unidade_next;
end if;
end process;
Abaixo estão três formas distintas de implementar um registrador de deslocamento, todas com a mesma funcionalidade, mas com diferenças na legibilidade, generalização e estilo de codificação:
r_next <= d & r_reg(3 downto 1);
q <= r_reg(0);
Essa abordagem é compacta e permite facilmente estender o registrador para um número genérico de bits.
r_next(3) <= d;
r_next(2) <= r_reg(3);
r_next(1) <= r_reg(2);
r_next(0) <= r_reg(1);
q <= r_reg(0);
Apesar de ser explícita e didática, essa forma não é escalável, pois exige reescrever todas as atribuições para cada nova largura de registrador.
r_next(3) <= d;
l1: for i in 2 downto 0 generate
r_next(i) <= r_reg(i + 1);
end generate;
q <= r_reg(0);
Esta versão combina clareza com generalização. Assim como a primeira, é facilmente adaptável para registradores de qualquer tamanho. Observação: A primeira e a terceira implementações são mais adequadas quando se deseja gerar registradores de deslocamento de largura genérica, facilitando a reutilização do código. Já a segunda, apesar de simples, não é prática para registros maiores, pois não é escalável.
-- Note que neste caso o '''label''' é obrigatório
label: FOR identificador IN faixa GENERATE
[Parte_Declarativa
BEGIN]
afirmação_concorrente;
afirmação_concorrente;
...
END GENERATE [label];
[rótulo:] FOR identificador IN faixa LOOP
afirmação_sequencial;
afirmação_sequencial;
...
END LOOP [rótulo];
Figura 5.8 - Technology Map de Shift Register ![]() Figura 5.9 - Simulação de Shift Register
Novas dicas para representar N dígitos BCD, cada um com 4 bits, será necessário criar um novo tipo de dado: type bcd_digits is array (natural range <>) of unsigned(3 downto 0);
signal bcd : bcd_digits(0 to N-1);
library work;
use work.nome_do_pacote.all;
Figura 5.22 - Temporizações de um Flip Flop ![]() Nota: a violação do tempo de setup ou hold leva ao estado de meta-estabilidade, na qual a saída fica temporariamente em um valor indefenido.
Figura 5.23 - Modelo de Circuito Sequencial ![]() Figura 5.24 - Tempo de Setup e Fmax ![]() Figura 5.25 - Equações do Tempo de Setup e Fmax (1/Tc(min)) ![]() Figura 5.26 - Equações do Tempo de Hold ![]() Figura 5.27 - Atraso da saída ![]()
Instruções do tipo LOOP: LOOP incondicional, FOR-LOOP, WHILE-LOOP, NEXT, EXIT
[rótulo:] LOOP
afirmação_sequencial;
afirmação_sequencial;
...
END LOOP [rótulo];
[rótulo:] WHILE condição LOOP -- Executa as "afirmações enquanto a "condição" for verdadeira
afirmação_sequencial;
afirmação_sequencial;
...
END LOOP [rótulo];
[rótulo:] [FOR identificador IN faixa] LOOP
afirmação_sequencial;
EXIT [rótulo] [WHEN condição]; -- Se a "condição" é verdadeira, termina o "LOOP"
afirmação_sequencial;
...
END LOOP [rótulo];
[rótulo:] [FOR identificador IN faixa] LOOP
afirmação_sequencial;
NEXT [rótulo] [WHEN condição]; -- Se a "condição" é verdadeira, não executa as linhas até a linha "END LOOP"
-- e incrementa o "identificador".
afirmação_sequencial;
...
END LOOP [rótulo];
[rótulo opcional:] CASE expressão IS
WHEN valor => -- valor único
afirmação_sequencial;
afirmação_sequencial;
...
WHEN valor1 | valor2 | ... | valorN => -- lista de valores
afirmação_sequencial;
afirmação_sequencial;
...
WHEN valor1 TO valor2 => -- faixa de valores
afirmação_sequencial;
afirmação_sequencial;
...
WHEN OTHERS => -- para evitar latches
afirmação_sequencial;
afirmação_sequencial;
...
END CASE;
entity leading_zeros is
generic (N : natural := 8);
port
( ________ : in std_logic_vector(0 to N-1);
count : out integer range 0 to N
);
end entity;
architecture ____ of leading_zeros is
begin
process (data)
variable count : integer ____ 0 to N
begin
count := 0;
for i ___ data'range ____
case data(i) is
when '0' => count := count + 1;
when _____ => exit;
end ___
end ____
zeros <= count;
end process;
end _______;
Figura 5.15 - Simulação do leading_zeros ![]()
Figura 5.16 - Simulação do counting_zeros ![]()
entity div_clk is
port(
clk: in std_logic;
reset: in std_logic;
clk_1ms: out std_logic;
clk_10sec: out std_logic;
clk_1sec: out std_logic
);
end entity;
Figura 3.17 - Contador de segundos e minutos - clock derivado ![]()
Figura 3.18 - Contador de segundos e minutos - clock síncrono ![]()
Comparar o desempenho Fmax, número de elementos lógicos, número de registradores, número de pinos dos seguintes contadores, considerando uma saída de N_bits.
b_next <= b_reg+1;
b <= g_reg xor ('0' & b(WIDTH-1 downto 1)); b1 <= b+1; g_next <= b1 xor ('0' & b1(WIDTH-1 downto 1));
r_next <= r_reg(0) & r_reg(WIDTH-1 downto 1);
r_next <= (not r_reg(0)) & r_reg(WIDTH-1 downto 1);
fb <= r_reg(1) xor r_reg(0); r_next <= fb & r_reg(3 downto 1);
buf_next <= '1' when (r_reg<unsigned(w)) or (w="0000") else '0';
Figura 5.X - RTL do circuito de PWM ![]() Na simulação abaixo, um clock de 50 ps foi utilizado, e o valor da entrada w foi definida como (3, 8, 0, 1, 15) a cada 1600 ps. Note que a largura do pulso da saída pwm_pulse está de acordo com aquela entrada. Figura 5.X - Simulação do circuito de PWM ![]()
Figura 5.13 - Simulação do contador decrescente 5 a 0 ![]()
Figura 5.14 - Simulação do contador decrescente 5 a 0 com parada ![]()
<generate_label>:
if <condition> generate
-- Concurrent Statement(s)
end generate;
Essa instrução é utilizada para a geração condicional de código durante a elaboração. Com isso, partes do projeto são sintetizadas e outras não, dependendo de certas condições ou parâmetros definidos no projeto. Como ela faz parte do grupo de instruções concorrentes, ela não pode ser utilizada dentro de PROCESS, FUNCTION ou PROCEDURE. Para exemplificar, consideramos a situação em que um contador crescente (UP) OU um contador decrescente (DOWN) devem serm utilizados, mas o contador nunca será aplicado nas duas direções de contagem. Neste caso, é possível estabelecer um parâmetro GENERIC, que definirá se a contagem é UP ou DOWN. Analise o código a seguir escrito em segmento único e usando variável: entity contador_up_down_if_generate is
generic
(
MIN : natural := 3;
MAX : natural := 21;
UPDOWN : natural := 0 -- 0 => up; 1 => down
);
port
(
clk, rst : in std_logic;
cnt_out : out integer range MIN to MAX -- Aqui deveria ser usado std_logic_vector
);
end entity;
architecture ifsc_v1 of contador_up_down_if_generate is
begin
L1: if UPDOWN = 0 generate
process (clk, rst)
variable cnt : integer range MIN to MAX;
begin
if rst = '1' then
cnt := MIN;
elsif (rising_edge(clk)) then
if cnt = MAX then
cnt := MIN;
else
cnt := cnt + 1;
end if;
end if;
cnt_out <= cnt;
end process;
end generate;
L2: if UPDOWN = 1 generate
process (clk, rst)
variable cnt : integer range MIN to MAX;
begin
if rst = '1' then
cnt := MAX;
elsif (rising_edge(clk)) then
if cnt = MIN then
cnt := MAX;
else
cnt := cnt - 1;
end if;
end if;
cnt_out <= cnt;
end process;
end generate;
end architecture;
Agora analise o mesmo contador descrito em dois segmentos, parte sequencial e parte de logica de próximo estado: architecture ifsc_v2 of contador_up_down_if_generate is
signal reg_cnt, next_cnt : integer range MIN to MAX;
begin
-- Contagem crescente
L1: if UPDOWN = 0 generate
-- Segmento sequencial
process (clk, rst)
begin
if rst = '1' then
reg_cnt <= MIN;
elsif rising_edge(clk) then
reg_cnt <= next_cnt;
end if;
end process;
-- Segmento de próximo estado
next_cnt <= MIN when reg_cnt = MAX else reg_cnt + 1;
end generate;
-- Contagem decrescente
L2: if UPDOWN = 1 generate
-- Segmento sequencial
process (clk, rst)
begin
if rst = '1' then
reg_cnt <= MAX;
elsif rising_edge(clk) then
reg_cnt <= next_cnt;
end if;
end process;
-- Segmento de próximo estado
next_cnt <= MAX when reg_cnt = MIN else reg_cnt - 1;
end generate;
-- Código de saída
cnt_out <= reg_cnt;
end architecture;
Se o parâmetro UPDOWN for instanciado com 0, teremos um contador crescente. Caso seja instanciado com 1, o contador será decrescente. Veja a simulação nos dois casos. Figura 5.20 - Simulação do contador UPDOWN com 0 ![]() Figura 5.21 - Simulação do contador UPDOWN com 1 ![]()
entity contador_up_down_two_arch is
generic
(
MIN : natural := 3;
MAX : natural := 21
);
port
(
clk, rst : in std_logic;
count_out : out integer range MIN to MAX
);
end entity;
architecture arch_up of contador_up_down_two_arch is
begin
process (clk, rst)
variable cnt : integer range MIN to MAX;
begin
if rst = '1' then
cnt := MIN;
elsif (rising_edge(clk)) then
if cnt = MAX then
cnt := MIN;
else
cnt := cnt + 1;
end if;
end if;
count_out <= cnt;
end process;
end architecture;
architecture arch_down of contador_up_down_two_arch is
begin
process (clk, rst)
variable cnt : integer range MIN to MAX;
begin
if rst = '1' then
cnt := MAX;
elsif (rising_edge(clk)) then
if cnt = MIN then
cnt := MAX;
else
cnt := cnt - 1;
end if;
end if;
count_out <= cnt;
end process;
end architecture;
configuration ifsc_cfg of contador_up_down_2arch is
-- for arch_up end for;
for arch_down end for;
end configuration;
entity contador_up_down_pin is
generic
(
MIN : natural := 3;
MAX : natural := 21
);
port
(
clk, rst : in std_logic;
UPDOWN : natural := 1; -- 0 => up; 1 => down
count_out : out integer range MIN to MAX
);
end entity;
architecture ifsc_v1 of contador_up_down_pin is
signal cnt_down, cnt_up : integer range MIN to MAX;
begin
process (clk, rst)
variable cnt : integer range MIN to MAX;
begin
if rst = '1' then
cnt := MIN;
elsif (rising_edge(clk)) then
if cnt = MAX then
cnt := MIN;
else
cnt := cnt + 1;
end if;
end if;
cnt_up <= cnt;
end process;
process (clk, rst)
variable cnt : integer range MIN to MAX;
begin
if rst = '1' then
cnt := MAX;
elsif (rising_edge(clk)) then
if cnt = MIN then
cnt := MAX;
else
cnt := cnt - 1;
end if;
end if;
cnt_down <= cnt;
end process;
count_out <= cnt_up when UPDOWN = 1 else cnt_down;
end architecture;
Ao fazer as mesmas perguntas para a mesma IA, as respostas podem ser diferentes. Por isso, todas respostas de IA deve passar por um processo de curadoria por humanos.
entity timer_seg is
generic (DEZENA : natural :=5; UNIDADE : natural := 9; FCLK := 5); -- para simulaçao
port (
CLK50MHZ, RESET : in std_logic;
CONTAR : std_logic;
ZERAR : std_logic;
LED_SEG : out std_logic;
SSD_DEZENA : out std_logic_vector(6 downto 0);
SSD_UNIDADE : out std_logic_vector(6 downto 0);
);
end entity;
architecture ifsc of top_level is
begin
end architecture;
Figura 5.17 - RTL timer_sec ![]() A seguir o resultado da simulação do timer_sec de segundos desde 00 até 59 segundos. No próximo segundo é feito o overflow, retornando a 00. O teste da entrada CONTAR é feito mantendo ela ativa inicialmente, e desativando-a entre 7,5 e 12,3 segundos, o que para a contagem. Para testar a entrada ZERAR, ela é atividada no segundo 86,3, zerando o contador na próxima borda do clock. A contagem reinicia em 87 segundos quando o sinal ZERAR é desativado. O sinal RESET é apenas usado no início da simulação para inicializar os registradores. Figura 5.18 - Simulação do timer_sec ![]() Figura 5.19 - Simulação do div_clk ![]() Figura 5.20 - Simulação do conta_bcd ![]() Figura 5.21 - Simulação do bcd2ssd (Catodo Comum) ![]() Figura 5.22 - Simulação do bcd2ssd (Anodo Comum) ![]()
|
1.5 Unidade 4 - Maquinas de Estado Finitas
Unidade 4 - Maquinas de Estado Finitas |
---|
Figura 4.1 - Exemplo de diagrama de estados de uma FSM Fig4.1(a) e implementação em hardware da FSM Fig4.2(a) ![]()
LIBRARY ieee;
USE ieee.std_logic_1164.ALL;
----------------------------------------------------------
ENTITY < entity_name > IS
PORT (
clk, rst : IN STD_LOGIC;
entradas : IN < data_type > ;
saidas : OUT < data_type >);
END entity;
----------------------------------------------------------
ARCHITECTURE < architecture_name > OF < entity_name > IS
TYPE state IS (A, B, C, ...);
SIGNAL pr_state, nx_state : state;
-- ATTRIBUTE ENUM_ENCODING : STRING; --optional attribute
-- ATTRIBUTE ENUM_ENCODING OF state : TYPE IS "sequential";
-- ATTRIBUTE SYN_ENCODING OF state : TYPE IS "safe";
BEGIN
------Logica Sequencial da FSM:------------
PROCESS (clk, rst)
BEGIN
IF (rst = '1') THEN
pr_state <= A;
ELSIF rising_edge(clk) THEN
-- apenas na borda do "clk" ocorre a mudança de estado da FSM
pr_state <= nx_state;
END IF;
END PROCESS;
------Logica Combinacional da FSM:------------
PROCESS (pr_state, entradas)
BEGIN
------Valores default das saidas------------
saidas <= < valor > ;
CASE pr_state IS
WHEN A =>
-- é necessário um WHEN para definir as "saidas" durante cada estado
-- e analisar as "entradas" para definir o próximo estado
saidas <= < valor > ; -- apenas se diferente do valor default
IF (entradas = < valor >) THEN
nx_state <= B;
...
ELSE
nx_state <= A;
END IF;
WHEN B =>
saidas <= < valor > ; -- apenas se diferente do valor default
-- dependendo das "entradas", pode ser que hajam mais de um estados de destino
IF (entradas = < valor >) THEN
nx_state <= C;
ELSIF (entradas = < valor >) THEN
nx_state <= A;
ELSE
nx_state <= B;
END IF;
WHEN C =>
saidas <= < valor > ; -- apenas se diferente do valor default
-- a passagem para outro estado pode não depender de nenhuma "entrada"
nx_state <= D;
WHEN ...
END CASE;
END PROCESS;
------Seção de Saída (opcional):-------
-- Essa seção visa garantir que a saida new_output esteja sincronizada com o clk.
-- Se isso não for importante, ela pode ser suprimida
PROCESS (clk, rst)
BEGIN
IF (rst = '1') THEN
new_output <= < valor > ;
ELSIF rising_edge(clk) THEN --or falling_edge(clk)
new_output <= output;
END IF;
END PROCESS;
END architecture;
WHEN others =>
nx_state <= <initial_state>;
Figura 4.2 - FSM - Controlador semafórico para duas vias (diagrama de estados) ![]()
Figura 4.3 - Diagrama de blocos de um FSM ![]()
Figura 4.4 - Blocos de um diagrama ASM ![]()
Figura 4.5 - Diagrama de estados de um controlador de memória ![]() Figura 4.6 - ASM de um controlador de memória ![]()
library ieee;
use ieee.std_logic_1164.all;
entity mem_ctrl is
port (
clk : in std_logic;
reset : in std_logic;
mem, rw, burst : in std_logic;
oe, we, we_me : out std_logic
);
end entity;
architecture two_seg_arch2 of mem_ctrl is
type mc_state_type is
(idle, read1, read2, read3, read4, write1);
signal state_reg, state_next : mc_state_type;
begin
process (clk, reset)
begin
if (reset = '1') then
state_reg <= idle;
elsif rising_edge(clk) then
state_reg <= state_next;
end if;
end process;
process (state_reg, mem, rw, burst)
begin
oe <= '0';
we <= '0';
we_me <= '0';
case state_reg is
when idle =>
if mem = '1' then
if rw = '1' then
state_next <= read1;
else
we_me <= '1';
state_next <= write1;
end if;
else
state_next <= idle;
end if;
when write1 =>
we <= '1';
state_next <= idle;
when read1 =>
oe <= '1';
if (burst = '1') then
state_next <= read2;
else
state_next <= idle;
end if;
when read2 =>
oe <= '1';
state_next <= read3;
when read3 =>
oe <= '1';
state_next <= read4;
when read4 =>
oe <= '1';
state_next <= idle;
end case;
end process;
end architecture;
Fonte: adaptado [2]. Figura 4.7 - RTL do controlador de memória ![]() Figura 4.8 - FSM do controlador de memória ![]()
Na figura abaixo a FSM modelada tem duas saídas Moore ("oe" e "we"), e uma saída Mealy ("we_me"). Note que as saídas do tipo Moore sempre tem a duração de um período de clock, e elas não dependem diretamente de nenhuma entrada, mas são consequência do estado em que se encontra a FSM. No caso da saída Mealy, as entradas afeta diretamente a saída, conforme se pode ver os gliches da entrada "mem" são transmitidas para a saída "we_me". Figura 4.9 - Simulação do controlador de memoria - diferença entre a saída Moore e saída Mealy ![]() Figura 4.10 - Simulação de escritas, leituras simples e leitura em burst no controlador de memoria ![]()
Figura 4.11 - Diagram de estados do detector de borda - implementação com saídas Moore ![]() library ieee;
use ieee.std_logic_1164.all;
entity edge_detector1 is
port(
clk, reset: in std_logic;
strobe: in std_logic;
p1: out std_logic
);
end edge_detector1;
architecture moore_arch of edge_detector1 is
type state_type is (zero, up, one);
signal state_reg, state_next: state_type;
begin
-- state register
process(clk,reset)
begin
if (reset='1') then
state_reg <= zero;
elsif (clk'event and clk='1') then
state_reg <= state_next;
end if;
end process;
-- next-state logic
process(state_reg,strobe)
begin
case state_reg is
when zero=>
if strobe= '1' then
state_next <= up;
else
state_next <= zero;
end if;
when up=>
if strobe= '1' then
state_next <= one;
else
state_next <= zero;
end if;
when one =>
if strobe= '1' then
state_next <= one;
else
state_next <= zero;
end if;
end case;
end process;
-- Moore output logic
p1 <= '1' when state_reg=up else
'0';
end moore_arch;
Fonte: [2]. Figura 4.12 - Simulação do detector de borda - implementação com saídas Moore ![]() Ao simular o detector de borda, percebe-se que o primeiro pulso (entre 140 e 180 ps) não foi detectado, pois não durou tempo suficiente para ter o estado atualizado no próximo clock. Isso ocorre pois na saída tipo Moore, as mudanças das saídas só acontecem sincronizadas com o clock. Note ainda que os pulsos em p1 tem a duração exata de um período de clock. Figura 4.13 - Diagram de estados do detector de borda - implementação com saídas Mealy ![]() library ieee;
use ieee.std_logic_1164.all;
entity edge_detector2 is
port(
clk, reset: in std_logic;
strobe: in std_logic;
p2: out std_logic
);
end edge_detector2;
architecture mealy_arch of edge_detector2 is
type state_type is (zero, one);
signal state_reg, state_next: state_type;
begin
-- state register
process(clk,reset)
begin
if (reset='1') then
state_reg <= zero;
elsif (clk'event and clk='1') then
state_reg <= state_next;
end if;
end process;
-- next-state logic
process(state_reg,strobe)
begin
case state_reg is
when zero=>
if strobe= '1' then
state_next <= one;
else
state_next <= zero;
end if;
when one =>
if strobe= '1' then
state_next <= one;
else
state_next <= zero;
end if;
end case;
end process;
-- Mealy output logic
p2 <= '1' when (state_reg=zero) and (strobe='1') else
'0';
end mealy_arch;
Fonte: [2]. Figura 4.14 - Simulação do detector de borda - implementação com saídas Mealy ![]() Ao simular o detector de borda, percebe-se que o primeiro pulso (entre 140 e 180 ps) agora foi detectado. Isso ocorre pois na saída tipo Mealy, as mudanças das saídas dependem do estado atual e da entrada, e por isso não são sincronizadas com o clock. Note que agora os pulsos em p1 tem duração variável, e menor que um período de clock.
Figura 4.15 - Diagram de estados do detector de borda - implementação com saídas Mealy melhorado ![]() |
1.6 Unidade 5 - Metodologia RT (Register Transfer)
Unidade 5 - Metodologia RT (Register Transfer) |
---|
A fonte principal dessa unidade é o capítulo 11 e 12 do livro "RTL Hardware Design Using VHDL: Coding for Efficiency, Portability, and Scalability, P. P. Chu." [2] É recomendado que os alunos leiam estes capítulos e usem como fonte de consulta.
# entrada a(0), a(1), a(2) e a(3) # saída out_q size = 4 sum = 0; for i in (0 to size - 1) do { sum = sum + a(i); } q = sum / 8; r = sum rem 8; if (r > 3) { q = q + 1; } out_q = q; O algoritmo primeiro soma os elementos individuais e armazena o resultado em uma variável chamada sum. Em seguida, usa as operações de divisão (/) e de resto (rem) para encontrar o quociente e o resto. Se o resto for maior que 3, adiciona-se 1 extra ao quociente para realizar o arredondamento.
sum(0) <= a(0); sum(1) <= sum(0) + a(1); sum(2) <= sum(1) + a(2); sum(3) <= sum(2) + a(3); q <= "000" & sum(3)(7 downto 3); r <= "00000" & sum(3)(2 downto 0); out_q <= q + 1 when (r > 3) else q;
As características principais dessa metodologia RT são:
rdest ← f(rsrc1, rsrc2, ...);
Figura 5.1 - Diagrama de blocos básico de uma FSMD ![]() 1.6.1 ATUAL
Nota: Este exemplo é apresentado no livro [2] na seção 11.3 Considere um multiplicador como os dois operandos da multiplicação sendo a_in e b_in, e a saída r_out onde todos os três sinais são do tipo UNSIGNED. Um algoritmo sequencial simples consiste em somar a_in repetidamente porb_in vezes. Por exemplo, 7×5 pode ser calculado como 7 + 7 + 7 + 7 + 7. Embora esse método não seja eficiente, ele é simples, e permite apreender a elaboração do diagrama ASMD e do hardware.
Esse algoritmo pode ser representado pelo pseudocódigo: if (a_in = 0 or b_in = 0) then r = 0 else a = a_in n = b_in r = 0 while (n ≠ 0) r = r + a n = n - 1 r_out = r Como o diagrama ASMD não possui uma construção de laço (loop), mas usa uma caixa de decisão com uma condição booleana para escolher um entre dois caminhos de saída possíveis. Portanto é semelhante a uma combinação de if com instrução goto. O pseudocódigo revisado fica assim: if (a_in = 0 or b_in = 0) then r_out = 0 else a = a_in n = b_in r = 0 op: r = r + a n = n - 1 if (n = 0) then goto stop else goto op stop: r_out = r
Observe na ASMD abaixo que os sinais start (início) e ready (pronto) foram adicionados para permitir a operação sequencial do sistema. Imaginando o multiplicador sequencial como parte de um sistema principal, quando ele deseja executar uma operação de multiplicação, primeiro verifica o sinal ready. Se estiver alto, coloca os dois operandos nas entradas de dados a_in e b_in e ativa o sinal start. Ao detectar o sinal start, o multiplicador sequencial captura os operandos e inicia o processamento. Após a conclusão do cálculo, o sinal ready é ativado para informar ao sistema principal que o resultado está disponível em r_out. O diagrama ASMD, mostrado na figura abaixo, segue de forma fiel o pseudoalgoritmo. Ele utiliza os registradores de dados n, a e r para representar as três variáveis do algoritmo, emprega caixas de decisão para implementar as estruturas condicionais (if) e utiliza operações de transferência entre registradores (operações RT) para executar as instruções sequenciais. Figura 5.2 - Diagrama ASMD do algoritmo multiplicador L11.1 ![]()
A construção do caminho de controle (CONTROL PATH) (1 - next state logic), (2 - state reg) e (3 - output logic) é a mesma utilizada com a máquina de estados (FSM). Os sinais dentro da caixa de decisão são as entradas da FSM. No diagrama ASMD, as expressões booleanas utilizam quatro sinais: start (comando externo) e a_is_0 (8), b_is_0(9) e count_0 (10) (sinais de status internos, provenientes do caminho de dados). Eles são ativados quando as condições correspondentes são satisfeitas. Os sinais (8) e (9) são resultado direto da comparação das entradas a_in = 0 e 'b_in = 0. O sinal (10) por utilizar um sinal interno necessita utilizar a entrada do registrador n (n_next = 0). A saída do caminho de controle inclui o sinal externo de status ready (pronto) e os sinais de controle que especificam as operações de transferência entre registradores (operações RT) no caminho de dados. Neste exemplo, foi usada a saída do registrador de estado como sinal de controle (idle, ab0, load, op), os quais controlam a seleção nos multiplexadores usados na rede de roteamento do caminho de dados.
A construção do caminho de dados pode ser derivada de forma sistemática seguindo diretrizes simples:
No exemplo da multiplicação por adição repetitiva as operações RT
Figura 5.3 - FSMD do algoritmo multiplicador L11.1 ![]()
Observando o ASMD acima, pode-se perceber que as saídas do estado ab0 e load são as mesmas, e portanto elas poderiam ser inseridas após a decisão start = 1, como saídas Mealy. Essa modificação produz um novo ASMD para o mesmo algoritmo, que apenas tem 2 estados. Figura 5.4 - Diagrama ASMD do algoritmo multiplicador L11.6 (saída Mealy) ![]()
|
2 Avaliações
Durante o semestre serão realizadas entre 2 avaliações e várias atividades feitas extra classe.
- Data das avaliações
- A1 : Avaliação A1 (peso 35) XX/XX/2025
- A2 : Avaliação A2 (peso 40) XX/XX/2025
- AEs : Média ponderada das AEs (peso 25)
- R1 e R2 : Recuperação de A1 e A2 :XX/XX/2025
- Folha de consulta de VHDL
3 Atividade relâmpago (AR)
As atividades relâmpago devem ser entregues no Moodle da disciplina. A não entrega dessas atividades não gera nenhum desconto, apenas geram pontos de BÔNUS que são adicionados aos conceitos das avaliações A1 a AN.
4 Atividade extra-classe (AE)
A média ponderada das atividades extra-classe será considerada no cálculo do conceito final da UC. A entrega das mesmas será feita pelo Moodle, e cada dia de atraso irá descontar 0,2 na nota da atividade. Muitas dessas atividades também geram pontos de BÔNUS que são adicionados aos conceitos das avaliações A1 a AN. Para os BÔNUS só serão considerados projetos entregues no prazo.
4.1 AE1 - Conhecendo os dispositivos lógicos programáveis
AE1 - Conhecendo os dispositivos lógicos programáveis |
---|
|
4.2 AE2 - Contador BCD 000 a 999
AE2 - Contador BCD 000 a 999 |
---|
entity contador_bcd is
Port ( clock : in std_logic;
reset : std_logic;
enable : std_logic;
centena : out std_logic_vector(3 downto 0);
dezena : out std_logic_vector(3 downto 0);
unidade : out std_logic_vector(3 downto 0)
);
end entity;
|
4.3 AE3 - Contador BCD genérico
AE3 - Contador BCD genérico |
---|
type bcd_digits is array (natural range <>) of unsigned(3 downto 0); signal bcd : bcd_digits(0 to N-1);
-- FILE : ifsc_pkg.vhd
library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;
package ifsc_pkg is
type bcd_digits is array (natural range <>) of unsigned(3 downto 0);
end package;
|
4.4 AE4 - Análise de contadores digitais
AE4 - Análise de contadores digitais |
---|
E1 - 5 e 15 bits E2 - 6 e 14 bits E3 - 7 e 13 bits E4 - 8 e 18 bits E5 - 7 e 10 bits E6 - 8 e 17 bits I1 - 9 bits I2 - 11 bits I3 - 12 bits I4 - 16 bits I5 - 13 bits I6 - 14 bits I7 - 18 bits I8 - 19 bits I9 - 17 bits
|
4.5 AE15 - Timer de Segundos com Mostrador SSD
AE15 - Timer de Segundos com Mostrador SSD |
---|
entity timer_seg is
generic (DEZENA : natural := 5; UNIDADE : natural := 9; FCLOCK : natural := 50);
port (
CLOCK50MHz : in std_logic;
RESET_PB : in std_logic;
CONTAR_SW : in std_logic;
ZERAR_PB : in std_logic;
LED_1SEC : out std_logic;
SSD_DEZENA : out std_logic_vector(6 downto 0);
SSD_UNIDADE : out std_logic_vector(6 downto 0));
end entity;
Arquitetura sugerida
architecture ifsc of timer_seg is
-- Sinais internos: BCD_UNIDADE, BCD_DEZENA, ENABLE_1SEC, etc.
-- Declaração dos componentes usados
begin
-- Instância do divisor de clock
-- Instância do contador BCD
-- Instâncias dos conversores BCD para SSD
end architecture;
Implemente o projeot no FPGA:
|
4.6 AE16 - Controlador de Semáforo de duas vias
5 Referências Bibliográficas:
- ↑ 1,0 1,1 1,2 1,3 1,4 PEDRONI, V. A. Circuit design and simulation with VHDL. 2nd. ed. New Delhi: PHI Learning Private, 2015.
- ↑ 2,00 2,01 2,02 2,03 2,04 2,05 2,06 2,07 2,08 2,09 2,10 2,11 2,12 2,13 2,14 2,15 CHU, P. P. RTL Hardware Design Using VHDL: Coding for Efficiency, Portability, and Scalability. 1a ed. [S.l]:Wiley-IEEE Press, 2006. 694p. ISBN 9780471720928.
Será que uma boa parte do que fazemos com calculo digital será analógico no futuro? Future Computers Will Be Radically Different (Analog Computing)