Brain Dump

terça-feira, 3 de novembro de 2009

Ataque Cilônio!

No último post, nós vimos como funciona uma FPGA. Agora é hora de aprender a usá-la na prática, e o melhor jeito é fazendo um pequeno projetinho. Como exemplo de projetinho, eu pensei que poderíamos construir um Cilônio a partir do zero:



Para começar a desenvolver para FPGAs, a primeira coisa de que você precisa é, obviamente, uma FPGA. Se você for o tipo de ninja que consegue soldar SMD na mão, pode fazer sua própria placa de desenvolvimento. Eu, como sou inepto com ferro de solda, optei por comprar um kit de desenvolvimento na Digilent. O kit que escolhi foi o S3EBOARD, que tem o menor custo/benefício:



Essa placa custa 150 dólares. Se você comprar do Brasil, somando o frete e impostos, pode ficar meio salgado, mas no final vale a pena. Uma FPGA sozinha não serve pra muita coisa, a coisa só fica divertida quando você a usa pra controlar algum dispositivo. E essa placa da Digilent tem todos os dispositivos que você precisa! Ela tem RAM, conector VGA, PS/2 pra teclado, serial pra mouse, conector Ethernet, DACs pra fazer saída de som, enfim, com ela você pode fazer um computador completo.

Em especial, no cantinho da placa tem uma série de 8 LEDs. Com isso, já podemos escolher por onde vamos começar a fazer o nosso Cilônio. Já que temos LEDs enfileirados, vamos começar pelo visor!



Já temos uma placa com a FPGA, precisamos agora de um compilador para programá-la. Eu uso o Xilinx ISE Webpack, que é free (as in beer), e disponível para Windows e Linux. Compiladores de FPGA são análogos aos compiladores de software, até mesmo na idéia de compilar em etapas. Um compilador típico de C tem estágios de pré-processamento, compilação, otimização e linking. Um compilador para FPGA possui as seguintes etapas:

Síntese: Tudo começa com a sua descrição do projeto, feita em alguma linguagem de descrição de hardware. Para começar o processo, o compilador traduz essa linguagem para uma representação RTL, que é basicamente um sistema de equações booleanas. Essas equações são então simplificadas, de modo a eliminar redundâncias e deixar o circuito final mais rápido.


Circuito original, e circuito após otimização

Mapeamento tecnológico: Mesmo que o otimizador tenha chegado nas equações booleanas mínimas, elas ainda não podem ser usadas na FPGA. Nós sabemos que a FPGA é formada de blocos lógicos (os CLBs), então precisamos de um processo para mapear as equações nos CLBs. Note que a etapa de síntese não depende da arquitetura, mas na etapa de map o compilador precisa saber o modelo exato da FPGA sendo usada, já que cada FPGA tem um tipo de CLB diferente.


Mapeamento tecnológico, cada CLB tem um AND e um OR

Place and Route: Depois de feito o mapeamento tecnológico, o compilador sabe quantas CLBs ele precisa, e como preencher cada uma delas. Mas ele ainda não escolheu quais CLBs serão usadas. Apesar das CLBs serem todas iguais, se você utilizar CLBs muito distantes entre si, o tempo que o sinal leva de uma a outra pode influir na velocidade do seu circuito. Na etapa de place and route, o compilador escolhe a melhor disposição de CLBs para o seu design.


Antes e depois do place and route

Geração da bitstream: Por fim, o compilador gera uma bitstream que é usada pra programar a FPGA, usualmente através de uma interface JTAG. A partir desse ponto, sua FPGA está programada e pronta pra rodar :)



Agora precisamos escolher a linguagem que vamos usar. O Xilinx Webpack suporta VHDL e Verilog. Escolher uma das duas é como escolher vi ou emacs, existem fãs dos dois lados, mas na prática são quase equivalentes. Em geral, para quem está começando, eu recomendo não pensar muito e escolher aquela que seus amigos usam (assim você tem pra quem perguntar quando tiver dúvidas). Eu acabei escolhendo usar VHDL.

A origem da linguagem VHDL é curiosa. O departamento de defesa americano fazia muitos contratos de fabricação de chips, mas a documentação que vinha de cada fabricante era diferente. Eles então bolaram um padrão, o VHDL, que no começo era simplesmente um jeito de documentar circuitos. Em um certo ponto, notaram que a documentação era precisa o suficiente pra permitir simulação, e da simulação partiram para a síntese.

Mas vamos ao nosso projeto. A primeira parte de um design em VHDL, assim como em faríamos em software, são os imports:



O motivo de incluir essas bibliotecas é que sem elas não podemos usar bits em hardware. Bits em software são simples, eles podem ser 0 ou 1. Já em hardware, temos muito mais opções. Além do "0" (pino ligado no GND) e do "1" (pino ligado no Vcc), também temos o "Z" (pino não conectado), "H" (pino ligado no Vcc através de um pull-up), e assim por diante. Em VHDL, bits de hardware são chamados de std_logic.


Alguns estados do std_logic

Em seguida nós precisamos definir a entidade que vamos fazer. Uma entidade em VHDL é como uma interface em Java, ela define como seu código interage com o mundo. Em hardware, isso é equivalente a definir quem serão os pinos de entrada e saída do seu design. No nosso projeto, nós precisamos de duas entradas: um clock e um botão de liga-desliga; e oito saídas: uma para cada LED.



Agora vamos definir a implementação da interface, que em VHDL é o behaviour. No nosso projeto, vamos usar um clock de 50MHz, rápido demais para fazer a animação dos LEDs. Precisamos então dividir essa freqüência, pra isso vamos usar um contador que reseta a cada 5 milhões de ciclos. Cada vez que o contador resetar, nós incrementamos um estado da nossa máquina de estados, e, por fim, vamos associar cada estado a uma combinação de LEDs acesos e apagados.

Se fosse software, precisaríamos de três variáveis pra implementar esse algoritmo. Em hardware, não usamos variáveis, mas sim registradores. Uma parte curiosa do VHDL é que você não define os registradores diretamente; ao invés disso, você define os sinais e ele infere quais registradores você precisa. Como você deve imaginar, num projeto grande isso é fonte de inúmeros bugs! Felizmente, nesse projetinho não vamos ter esse problema.



Note que ao especificar um registrador, você precisa falar o tamanho dele. Nosso contador de clocks, por exemplo, vai de 0 a 5 milhões, e portanto cabe num registro de 23 bits. Vamos aproveitar e implementar esse contador. A diferença mais importante de hardware e software é que no hardware você tem paralelismo real, não é time-sharing e nem tem limite no número de cores. Em VHDL, cada unidade que roda em paralelo é um processo:



Na primeira linha de cada processo você define quem são as entradas (nesse caso, o clock e o valor anterior do contador). O primeiro if checa se aconteceu uma mudança no clock que o levou para o estado 1 (ou seja, o código dentro do if só roda em uma borda de subida). O resto é como em software, se chegar a 5M, volte a zero, senão incremente.

O processo seguinte faz o mesmo com o contador do estado atual:



O contador de estado incrementa na borda de subida, mas só quando o contador de clock é zero. Além disso, nós também zeramos o estado quando a chave SW0 está desligada (implementando assim um botão que liga e desliga).

Agora precisamos associar cada estado a uma combinação de LEDs (o que, em hardware, chamamos de demultiplexação):



Note que além de definir todos os estados possível, ainda temos um else extra. Precisamos sempre lembrar que, em hardware, bits não são apenas 0 ou 1! Se o estado cair em algum dos outros valores, como Z ou H, precisamos do else extra para cuidar dele.

Por fim, precisamos ligar o demultiplexador nos pinos de saída. Ligações simples de sinais nem precisam de um processo:



Agora o VHDL está pronto, mas ainda falta um detalhe para acabar o projeto. O VHDL cuida de tudo que vai acontecer dentro da FPGA, mas ainda precisamos cuidar do que está fora dela. Pra isso, precisamos de um constraint file, que vai indicar em quais pinos da FPGA estão ligados nossos periféricos (os LEDs, o clock, etc). Esses valores nós pegamos diretamente do manual do kit da Digilent. Nossos arquivos ficaram assim, então:

Visor cilônio em VHDL
Visor cilônio (constraint file, no formato UCF)

Com tudo pronto, só precisamos compilar e fazer o upload para a FPGA :)







Agora temos o visor pronto! Só falta terminar o resto do Cilônio, que fica como exercício para o leitor :)

Marcadores: , , ,

terça-feira, 13 de outubro de 2009

Stallman e o hardware livre

Hoje em dia, é difícil achar quem trabalhe com computação e não saiba quem é o Richard Stallman. A primeira vez que vi o nome dele foi lendo os sources do bison, e não demorou muito pra descobrir que ele era o cabeça por trás de vários softwares que eu usava, como o gcc e o emacs. E não apenas isso, ele também foi o criador do conceito de software livre e do projeto GNU.

Mas mesmo com todo o seu talento, o Stallman não escapa de ser uma espécie de Michael Jackson da computação. Embora seja inegável que ele é um gênio que influenciou toda uma indústria, também não há como negar que ele não é exatamente um modelo de conduta. Na verdade, conta-se que pessoalmente ele é bastante desagradável, e sua higiene pessoal é de deixar o Cascão com inveja.


Além disso, de vez em quando o Stallman também vacila. Por exemplo, em 1999 perguntaram pra ele se algum dia seria possível ter hardware livre, assim como havia o software livre. Ele respondeu com um artigo sobre o assunto, sendo que em um dado ponto ele escreve que "você não pode baixar hardware pela net, e nós não temos copiadores automáticos para hardware (talvez quando tivermos nanotecnologia)".

O vacilo do Stallman é que você não precisa de nanotecnologia, desde 1985 já existe uma tecnologia que permite o surgimento do hardware livre: a FPGA.

A FPGA é um chip reprogramável. Ela sai da fábrica vazia, mas você pode reprogramá-la pra virar, por exemplo, uma CPU. Se você mudar de idéia, pode reprogramá-la mais uma vez, e ela vira uma memória. Ou um processador de vídeo, ou um controlador de rede, o que der na telha!

Pra entender como isso é possível, vamos voltar lá pra linha de produção. Chips são feitos em um substrato de silício apelidado de bolacha. O mapa com os circuitos que compõem o chip é impresso sobre a bolacha com um processo químico parecido com a revelação de filmes fotográficos. A parte importante é notar que esse processo é uma espécie de carimbo: você faz centenas de cópias iguais do circuito sobre a bolacha, e, depois de carimbado, não dá mais pra mudar o que foi impresso.


Bolacha (wafer)

Mas se o circuito é carimbado e não pode ser mudado, como podemos reprogramar o hardware? Há pelo menos duas maneiras de fazer isso. Para exemplificar, vamos imaginar que queremos implementar uma função booleana qualquer, como a função abaixo:


S = ((not A and B) or (A and not B)) and C

Esse é um circuito combinatório simples, então podemos levantar a tabela da verdade dele:


Agora fica clara qual é a sacada, basta interpretar esse circuito como se fosse uma memória! No caso, a tripla ABC é como se fosse um bus de endereçamento para oito posições de memória, onde cada palavra tem um único bit: a saída S. Memórias reprogramáveis nós sabemos fazer, certo? Nosso circuito reprogramável pode ser implementado como uma simples memória RAM.

A segunda maneira é lembrando que existem portas lógicas que exibem universalidade. Aquela mesma equação booleana pode ser implementada usando apenas portas NAND:


Novamente, a idéia se apresenta. Podemos carimbar na bolacha um monte de portas NAND, e, para reprogramar basta mudar a conexão entre elas (por exemplo, usando uma RAM que funcionaria como se fosse um array de ponteiros).

As FPGAs utilizam uma mescla dos dois métodos. Elas são formadas de uma série de elementos repetidos, chamados CLB (configurable logic block). No modelo que eu uso em casa, cada CLB é formado de quatro slices, e esse aqui é o conteúdo de um terço de cada slice:


Parte de um slice da Spartan 3E

Os slices são levemente mais complicados que uma porta NAND, mas isso permite que você consiga aproveitar melhor a arquitetura. Às vezes, dá pra colocar um bom pedaço de lógica em um único slice. Além disso, se você prestar atenção no slice, tem um bloco chamado LUT (look-up table), que é essencialmente aquela RAMzinha do primeiro método.

Para completar, no canto direito tem um flip-flop tipo D. Até agora, estávamos falando apenas de circuitos combinatórios, mas FPGAs podem usar esses flip-flops para fazer circuitos sequenciais também. Alguns modelos de FPGA vão além, e possuem até mesmo DACs e PLLs que podem ser usados para sintetizar circuitos analógicos.


A FPGA também tem uma outra vantagem, que pra mim é fundamental. Embora eu adore eletrônica, sou muito ruim para soldar componentes (nunca aprendi como fazer isso tendo só dois braços). Mas as FPGAs não são programadas com soldas, ao invés disso, elas são programadas com descrições em texto, muito parecidas com as linguagens de programação. Após finalizar a descrição, que é uma espécie de código-fonte do hardware, você usa um compilador e faz upload para a placa, sem precisar de estanho e nem de ferro de solda.

E melhor, essas descrições em texto podem ser compartilhadas como qualquer arquivo texto. Aplique nelas uma licença apropriada, e temos então o hardware livre que queríamos! Hoje em dia, existem até repositórios de hardware livre, como o Open Cores, de onde você pode baixar de tudo, desde clones do Z80 até aceleradores de H264.

Se você ainda não conhecia FPGAs, deve estar bem curioso pra saber como você pode brincar com isso. No próximo post eu vou fazer um pequeno tutorial de como criar um circuito do zero, então fique ligado no blog :)

Marcadores: ,