Questão: Como era a programação na Era do ENIAC?
Ter o Quartus Prime Lite instalado e funcional. Ele está disponível no site da Intel:
Ter feito o Tutorial para início das Aulas: Uso do Quartus e VHDL, que está no blackboard em:
O ENIAC, criado por John Presper Eckert e John Mauchly entre 1943 e 1945 na Universidade da Pensilvania, foi considerado por muito tempo o primeiro computador de uso geral já construído. Porém, ele não possuía uma caracteristica muito importante, a capacidade de armazenar o programa sendo executado para uso posterior. O conceito de programa armazenado, foi implementado no sucessor do ENIAC (Electronic Numerical Integrator and Computer), o EDVAC (Electronic Discrete Variable Automatic Computer).
Características do ENIAC:
Principal finalidade: cálculos balísticos;
Consumia 150 kW de potência;
Tinha 18.000 válvulas;
Pesava aproximadamente 30 toneladas;
Ocupava 170 metros quadrados;
Processava, por segundo, 5.000 adições ou 357 multiplicações ou 38 divisões;
Entrada de dados através de cartões perfurados;
A saída de dados era dada por uma sequência de lâmpadas e um perfurador de cartões;
Não foi utilizado durante a segunda guerra mas participou nos cálculos da bomba de hidrogênio.
Na verdade, o ENIAC era uma coleção de unidades funcionais que eram interconectadas e configuradas para resolver um determinado problema matemático. Ou seja, a programação do ENIAC era feita através da conexão de cabos e configuração de chaves nos painéis dessas unidades funcionais.
Com o objetivo do programa definido, era feita a sua programação através de esquemas de conexão.
A programação do ENIAC era a criação de um caminho para os dados, ou fluxo de dados, que resolvesse o problema em questão. Uma atividade similar ainda é utilizada na criação de projetos digitais, mais especificamente, no projeto de processadores.
Curiosidade
O primeiro bug verdadeiro foi encontrado, em 09 de setembro de 1947, no computador Mark II, em Harvard. Fazia parte da equipe a criadora do primeiro compilador: Grace Murray Hopper.
Por exemplo, para criar, de forma semelhante ao que era feito no ENIAC, um caminho de dados que resolva a seguinte equação:
S = 2x - 3y + 7
Utilizaríamos unidades funcionais para executar as somas e outra para executar a subtração.
As unidades funcionais utilizam os operandos, indicados pelas letras A e B, da seguinte forma: A operação B.
Ou seja, no caso de se usar um subtrator, a saída dele será dada por: A - B.
Um grande inconveniente, similar ao que ocorria com o ENIAC, é a necessidade de reconectar (reprogramar) as unidades funcionais para resolver outra equação diferente.
Por exemplo, para a equação S = 3x + 3y - 5, teríamos o FD mostrado abaixo.
Além disso, para cada operação temos que usar uma unidade funcional nova, não há como reutilizar a mesma unidade várias vezes.
Responder o quiz de participação, no blackboard, em:
Conteúdos > Participação > Aula_1_Quiz-P1
Para otimizar a utilização dos componentes do fluxo de dados, podemos fazer com que ele seja configurável dinamicamente. Uma forma de fazer isso é utilizando elementos que memorizem os resultados intermediários e adicionar circuitos multiplex para reconfigurar o caminho de dados.
A configuração do FD é feita pela alteração dos sinais de controle de acordo com o cálculo que desejamos fazer. Por exemplo, para calcular o valor de 3x - 4, podemos utilizar a seguinte sequência passos de ativação dos pontos de controle.
Iniciamos fazendo o RESET (pulso no ponto de controle Reset A, permanecendo em 0 após o pulso) do registrador A. Isso fará com que esse registrador, também chamado de ACUMULADOR, tenha o valor 0.
Colocamos o valor de “x” na entrada B da ULA.
Selecionamos a soma como a operação da ULA a ser executada (ponto de controle Operação = 1).
Selecionamos a entrada 1 do MUX, para que o resultado da operação seja armazenado no Acumulador (ponto de controle Sel MUX = 1).
Ativamos o Acumulador (ponto de controle Habilita A = 1), para que o resultado da soma seja escrito no Acumulador no momento da subida do próximo clock.
Como o clock ainda não ocorreu, após o tempo de propagação dos sinais pelo FD, teremos a entrada do Acumulador com o valor de “x” e a sua saída com o valor anterior (após o reset) 0.
No momento do pulso de clock, a saída do Acumulador é atualizada para o valor “x”.
De agora em diante, a cada pulso de clock adicionaremos “x” ao Acumulador. Ou seja, teremos o resultado acumulado das somas.
Este passo, como visto anteriormente, é o envio de um pulso de clock.
Após esse clock teremos o valor de “x+x+x” na entrada do Acumulador, como pode ser visto abaixo.
Este passo é o envio de um pulso de clock, para obter o resultado final das somas com “x” no Acumulador.
Neste passo, precisamos:
Mudar a entrada de dados para o valor 4 (entrada B da ULA).
Mudar a operação para realizar a subtração (ponto de controle Operação = 0).
Ao enviar o último pulso de clock, armazenamos no acumulador o valor 3X-4.
Responder o quiz de participação, no blackboard, em:
Conteúdos > Participação > Aula_1_Quiz-P2
Para tanto, utilizaremos uma ULA que possui somente a soma e a subtração, que está na ABA Modelos VHDL.
Os outros componentes utilizados também estão na ABA Modelos VHDL.
As características do circuito estão listadas abaixo:
Dados com largura de 4 bits;
As entradas x e y devem ser conectadas às chaves de entrada da placa DE0-CV:
As chaves SW3 até SW0 serão a variável y;
As chaves SW9 até SW6 serão a variável x;
A saída S (Resultado) deve ser conectada aos LEDs vermelhos.
É obrigatório a utilização de um detector de borda, também chamado de discriminador de borda, na entrada do botão de clock.
Para cada projeto, deve existir a definição da funcionalidade dos pinos da FPGA em um arquivo do tipo .qsf.
Durante a criação do projeto, o Quartus cria, na pasta principal do seu projeto, um arquivo chamado Nome_do_Projeto.qsf.
Ele contém as definições básicas do projeto e deve ser acrescentada a definição dos pinos. Essa definição está na ABA: Modelos VHDL > Kit DE0-CV > Arquivo de Configuração dos Pinos (.qsf) do Kit FPGA DE0CV para o Quartus.
Para confirmar o funcionamento do seu projeto, veja o circuito RTL resultante e faça a simulação.
Após o circuito funcionar adequadamente, grave-o na FPGA e faça alguns testes para confirmar o seu funcionamento.
Verifique o funcionamento do sinal Habilita A. O que ocorre se o sinal Habilita A estiver em nível 0 e ocorrer um pulso de clock?
Devido ao uso de 4 bits, é necessário restringir os números utilizados para evitar ultrapassar o limite representável pelos 4 bits (valor decimal = 15).
Esquema Completo (com entradas e saídas).
As conexões dos I/Os da placa DE0-CV são mostrado abaixo:
O seu projeto será formado por cinco arquivos, todos eles baseados na estrura do esqueleto (disponível na ABA Modelos VHDL), que são:
O top level, que é o arquivo principal (similar ao main da linguagem C), que conterá:
Todas as instâncias dos componentes a serem usados (cada modelo VHDL dos componentes possui um exemplo da sua instanciação);
A interligação entre esses componentes, também conhecido como port map de cada instância de componente;
A declaração e interligação dos recursos externos à FPGA com o circuitos que usam esses componentes. Isso é feito através do port da declaração da entity e do arquivo .qsf (disponível na ABA Referências).
Um arquivo para o MUX, chamado muxGenerico2x1.vhd, que será preenchido com o modelo VHDL de “MUX Genérico” (disponível na ABA Modelos VHDL);
Um arquivo para o Registrador, chamado registradorGenerico.vhd, que será preenchido com o modelo VHDL de “Registrador Genérico” (disponível na ABA Modelos VHDL);
Um arquivo para a ULA, chamado ULASomaSub.vhd, que será preenchido com o modelo VHDL de “ULA: Soma e Subtração” (disponível na ABA Modelos VHDL).
Um arquivo para o Detector de Borda, chamado edgeDetector.vhd, que será preenchido com o modelo VHDL de “Detector de Borda” (disponível na ABA Modelos VHDL).
O estado em repouso do Botão é o nível ALTO, portanto, para o RESET devemos usar not KEY(1).
Abaixo, temos um exemplo do top_level desta atividade, completo e funcional.
Note que, para interligar os compontentes, utilizamos a declaração signal, como mostrado no exemplo abaixo.
library ieee;
use ieee.std_logic_1164.all;
entity Aula1 is
-- Total de bits das entradas e saidas
generic ( larguraDados : natural := 4;
simulacao : boolean := TRUE -- para gravar na placa, altere de TRUE para FALSE
);
port (
CLOCK_50 : in std_logic;
KEY: in std_logic_vector(3 downto 0);
SW: in std_logic_vector(9 downto 0);
LEDR : out std_logic_vector(9 downto 0)
);
end entity;
architecture arquitetura of Aula1 is
-- Obs.:
-- SW(9 downto 6) : entrada da variavel X.
-- SW(3 downto 0) : entrada da variavel Y.
-- SW(4) : o ponto de controle da Operacao da ULA.
-- SW(5) : o ponto de controle de SelMUX.
-- KEY(0) : o botão do clock.
-- KEY(1) : o ponto de controle para Reset A (o Botao em repouso fica no nível alto, portanto, devemos usar not KEY(1) no Reset).
-- KEY(2) : o ponto de controle de Habilita A.
-- KEY(3) : sem uso.
signal chavesX_ULA_B : std_logic_vector (larguraDados-1 downto 0);
signal chavesY_MUX_A : std_logic_vector (larguraDados-1 downto 0);
signal REG1_ULA_A : std_logic_vector (larguraDados-1 downto 0);
signal Saida_ULA : std_logic_vector (larguraDados-1 downto 0);
signal MUX_REG_A : std_logic_vector (larguraDados-1 downto 0);
signal Chave_Operacao_ULA : std_logic;
signal CLK : std_logic;
signal SelMUX : std_logic;
signal Habilita_A : std_logic;
signal Reset_A : std_logic;
signal Operacao_ULA : std_logic;
begin
-- Instanciando os componentes:
-- Para simular, fica mais simples tirar o edgeDetector
gravar: if simulacao generate
CLK <= KEY(0);
else generate
detectorSub0: work.edgeDetector(bordaSubida)
port map (clk => CLOCK_50, entrada => (not KEY(0)), saida => CLK);
end generate;
-- Instancia do MUX.
MUX1 : entity work.muxGenerico2x1 generic map (larguraDados => larguraDados)
port map( entradaA_MUX => chavesY_MUX_A,
entradaB_MUX => Saida_ULA,
seletor_MUX => SelMUX,
saida_MUX => MUX_REG_A);
-- Instancia do Acumulador.
REG1 : entity work.registradorGenerico generic map (larguraDados => larguraDados)
port map (DIN => MUX_REG_A, DOUT => REG1_ULA_A, ENABLE => Habilita_A, CLK => CLK, RST => Reset_A);
-- Instancia da ULA:
ULA1 : entity work.ULASomaSub generic map(larguraDados => larguraDados)
port map (entradaA => REG1_ULA_A, entradaB => chavesX_ULA_B, saida => Saida_ULA, seletor => SW(4));
-- Chaves e Botoes.
chavesX_ULA_B <= SW(9 downto 6);
chavesY_MUX_A <= SW(3 downto 0);
SelMUX <= SW(5);
Operacao_ULA <= SW(4);
Reset_A <= not KEY(1);
Habilita_A <= KEY(2);
-- A ligacao dos LEDs:
LEDR (9) <= SelMUX;
LEDR (8) <= Habilita_A;
LEDR (7) <= Reset_A;
LEDR (6) <= Operacao_ULA;
LEDR (5) <= '0'; -- Apagado.
LEDR (4) <= '0'; -- Apagado.
LEDR (3 downto 0) <= REG1_ULA_A;
end architecture;
Para verificar se o projeto está conforme o desejado, podemos utilizar o visualizador RTL. No Quartus, ele está em:
Caso tenha mantido a linha de configuração para fazer simulação, como mostrado abaixo:
generic ( larguraDados : natural := 4;
simulacao : boolean := TRUE -- para gravar na placa, altere de TRUE para FALSE
);
O diagrama obtido deverá ser similar ao abaixo.
Caso tenha mantido a linha de configuração para executar na placa, como mostrado abaixo:
generic ( larguraDados : natural := 4;
simulacao : boolean := FALSE -- para gravar na placa, altere de TRUE para FALSE
);
O diagrama obtido deverá ser similar ao abaixo.
Com o circuito pronto, faça o teste utilizando a equação 3x - 4. A sequência dos pontos de controle são as mesmas mostradas no exemplo anterior.
Para facilitar, faça uma tabela com essa sequência, que reutilizaremos na próxima aula.
Esta Atividade deverá ser entregue através do Blackboard!