Ferramenta para Desenvolvimento de Sistemas - LaPSI

Transcrição

Ferramenta para Desenvolvimento de Sistemas - LaPSI
UNIVERSIDADE FEDERAL DO RIO GRANDE DO SUL
ESCOLA DE ENGENHARIA
INSTITUTO DE INFORMÁTICA
CURSO DE GRADUAÇÃO EM ENGENHARIA DE COMPUTAÇÃO
FRANCISCO ROBERTO PEIXOTO SOCAL
Ferramenta para Desenvolvimento de
Sistemas Embarcados Utilizando
Linguagem de Alto Nı́vel
Projeto de Diplomação
Prof. Dr. Altamiro Amadeu Susin
Orientador
Porto Alegre, dezembro de 2003
SUMÁRIO
LISTA DE FIGURAS . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
4
LISTA DE TABELAS . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
5
1 INTRODUÇÃO . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
1.1 A ferramenta proposta . . . . . . . . . . . . . . . . . . . . . . . . .
1.2 Organização deste documento . . . . . . . . . . . . . . . . . . . . .
6
7
7
2
DESENVOLVIMENTO DE SISTEMAS
EMBARCADOS . . . . . . . . . . . . .
2.1 Possibilidades de implementação .
2.2 Desenvolvimento de software . . .
2.3 Metodologias alternativas . . . . . .
.
.
.
.
.
.
.
.
.
.
.
.
. . . . . . . . . . . . .
. . . . . . . . . . . . . .
. . . . . . . . . . . . . .
. . . . . . . . . . . . . .
. 9
. 9
. 9
. 10
3 A LINGUAGEM LUA . . . . . . . . .
3.1 Aplicações . . . . . . . . . . . . . .
3.2 Implementação . . . . . . . . . . .
3.2.1
Conceituação . . . . . . . . . . . .
3.2.2
A Máquina Virtual Lua . . . . . . .
3.3 Utilização . . . . . . . . . . . . . . .
3.3.1
Utilização em Sistemas Embarcados
3.4 Exemplos de código . . . . . . . .
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
. . . . . . . . . . . . .
. . . . . . . . . . . . . .
. . . . . . . . . . . . . .
. . . . . . . . . . . . . .
. . . . . . . . . . . . . .
. . . . . . . . . . . . . .
. . . . . . . . . . . . . .
. . . . . . . . . . . . . .
.
.
.
.
.
.
.
.
12
13
13
13
13
14
14
15
4 A FERRAMENTA PICOLUA . . . . . . . . .
4.1 O uso da Máquina Virtual Lua . . . . .
4.2 Arquiteturas alvo e a geração de código
4.3 Implementação . . . . . . . . . . . . . . .
4.3.1
O módulo nula . . . . . . . . . . . . . . .
4.3.2
O analisador de código . . . . . . . . . . .
4.3.3
O gerador de código para PIC . . . . . . .
4.3.4
Extensões à linguagem Lua . . . . . . . . .
4.4 Metodologia proposta . . . . . . . . . . .
. . . . . . . . . . . . .
. . . . . . . . . . . . . .
. . . . . . . . . . . . . .
. . . . . . . . . . . . . .
. . . . . . . . . . . . . .
. . . . . . . . . . . . . .
. . . . . . . . . . . . . .
. . . . . . . . . . . . . .
. . . . . . . . . . . . . .
.
.
.
.
.
.
.
.
.
19
19
19
20
21
22
24
25
27
5 ESTUDOS DE CASO . . . . . . . . . . .
5.1 Multiplicação de inteiros . . . . . . .
5.2 Comunicação serial . . . . . . . . . . .
5.3 Sistema de medição de vibração . . .
5.4 Modelo para aplicações distribuı́das
. . . . . . . . . . . . .
. . . . . . . . . . . . . .
. . . . . . . . . . . . . .
. . . . . . . . . . . . . .
. . . . . . . . . . . . . .
.
.
.
.
.
29
29
31
31
32
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
5.4.1
Plataformas utilizadas
5.4.2
Conectividade . . . . .
5.4.3
Benefı́cios . . . . . . .
5.5 Resultados . . . . . . .
.
.
.
.
32
32
33
33
CONCLUSÃO . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
35
REFERÊNCIAS . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
37
APÊNDICES . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
38
6
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
LISTA DE FIGURAS
Figura 2.1: Fluxo de desenvolvimento de software
. . . . . . . . . . . . . . . 10
Figura
Figura
Figura
Figura
Figura
3.1:
3.2:
3.3:
3.4:
3.5:
Fluxo de desenvolvimento com a linguagem Lua . .
Cálculo da seqüência de Fibonacci em Lua . . . . .
Utilização de tabelas Lua como containers de dados
Suporte a orientação a objetos em Lua . . . . . . .
Extensão de semântica na linguagem Lua . . . . . .
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
14
15
16
17
18
Figura
Figura
Figura
Figura
Figura
Figura
4.1:
4.2:
4.3:
4.4:
4.5:
4.6:
Estruturação da ferramenta picoLua . . . . . . . . . . . . .
Mapa de utilização de memória no PIC . . . . . . . . . . . .
Exemplo de uso da tabela regs . . . . . . . . . . . . . . .
Exemplo de uso de assembly inline . . . . . . . . . . . . . .
Exemplo de definição do target e da palavra de configuração
Metodologia proposta com a ferramenta picoLua . . . . . .
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
20
25
26
26
27
28
Figura 5.1: Codificações em Lua, bytecodes Lua e assembly do PIC . . . . . . 30
Figura 5.2: Sistema tı́pico de medição de vibração . . . . . . . . . . . . . . . 31
Figura 5.3: Arquitetura para aplicações distribuı́das . . . . . . . . . . . . . . 33
LISTA DE TABELAS
Tabela 4.1: Representação de uma função Lua gerada pelo nula . . . . . . . . 21
Tabela 4.2: Representação de uma instrução gerada pelo nula . . . . . . . . . 22
Tabela 4.3: Comparação entre as metodologias picoLua e SASHIMI . . . . . . 27
Tabela 5.1: Comparação entre implementações em Lua e Assembly . . . . . . 34
6
1
INTRODUÇÃO
O avanço constante dos processos de fabricação de circuitos integrados tem possibilitado o desenvolvimento de sistemas computacionais cada vez mais complexos.
Neste cenário, destacam-se os sistemas embarcados que ganham espaço em produtos
de eletrônica de consumo, mas que em muitas vezes apresentam restrições de custos
ou de recursos disponı́veis. Como conseqüência, o projeto e o teste destes sistemas
têm se mostrado igualmente difı́ceis.
Neste sentido, existem diversas metodologias e ferramentas para projeto, descrição e sı́ntese de tais sistemas. Pode-se citar os ambientes de desenvolvimento,
com compiladores, simuladores, etc. Embora sejam freqüentemente especı́ficos para
determinadas tecnologias ou fabricantes (e conseqüentemente incompatı́veis entre
si), são freqüentes no cenário atual. Existe também a possibilidade de sı́ntese automática de hardware e software para integração em um SOC (System On a Chip),
dada uma descrição em uma linguagem de alto nı́vel.
Por outro lado, com o crescente aumento da complexidade dos softwares desenvolvidos, a engenharia de software se faz cada vez mais necessária. Busca-se o
desenvolvimento de software manutenı́vel e portável, mas também com rápida prototipação e com a possibilidade de ter seu teste integrado. Linguagens de programação
tradicionais como C++ e Java podem ser utilizadas para atingir tais ideais, mas deixam a desejar quando comparadas com as possibilidades oferecidas por linguagens
de mais alto nı́vel como Scheme, Python ou Lua. Caracterı́sticas tipicamente encontradas em linguagens interpretadas, como flexibilidade, facilidade de integração com
demais linguagens e programas, tratamento de funções como valores de primeira
classe e migração de código, podem se tornar caracterı́sticas interessantes no desenvolvimento e principalmente na prototipação e nos testes de sistemas embarcados.
Neste contexto, a linguagem de programação Lua destaca-se pela simplicidade
que oferece tais caracterı́sticas e também por ser extremamente portável e compacta (FIG 1996). Adicionalmente, a linguagem mostra-se interessante por ser software
livre, o que possibilita sua expansão e o desenvolvimento de trabalhos nela baseados.
Apesar de ser uma linguagem concebida originalmente para extensão de aplicações — onde tradicionalmente interpreta-se diretamente o código fonte — a linguagem Lua adota um abordagem mais eficiente, similar à de Smalltalk e Java. Foi
definida uma máquina virtual que atende às necessidades da linguagem e para a
qual os programas são compilados, gerando os chamados bytecodes. Os programas
são então executados através da simulação da máquina virtual, interpretando os
bytecodes gerados (FIG 1994).
Assim, obtém-se algumas vantagens, tanto sobre linguagens compiladas convencionais como sobre linguagens interpretadas. Pode-se citar a portabilidade dos
7
programas, desde que se tenha implementações da máquinas virtuais para as plataformas desejadas. Já sobre linguagens interpretadas, tem-se principalmente a maior
velocidade de execução, uma vez que a análise léxica e sintática do código fonte só
é feita uma vez. Fora isso, de posse da especificação da máquina virtual, pode-se
partir para abordagens alternativas, como a implementação de máquinas reais ou
como a recompilação de bytecodes para outras plataformas (STOL 2003).
1.1
A ferramenta proposta
Baseado nestes argumentos, este trabalho apresenta uma ferramenta para o desenvolvimento e testes de sistemas embarcados baseada na linguagem Lua. Nela são
exploradas as potencialidades da linguagem, sendo esta estendida e portada para
algumas plataformas computacionais.
Adicionalmente, uma vez que a linguagem Lua já tem sua máquina virtual implementada e suportada por uma vasta gama de arquiteturas que dispõem de sistemas
operacionais e demais recursos, optou-se pelo aprimoramento da ferramenta, tornando possı́vel o uso da linguagem em sistemas computacionais com recursos mı́nimos.
Por recursos mı́nimos entende-se a não disponibilidade de um sistema operacional
completo, com facilidades de gerência de memória, manipulação de arquivos, etc.
Assim, teve-se como alvo sistemas profundamente embarcados, sejam eles com microcontroladores, com cores sintetizáveis, ou mesmo em SmartCards e tecnologias
similares.
Como estudo de caso, optou-se por desenvolver a ferramenta visando a famı́lia de
microcontroladores PIC, da Microchip. Tal escolha foi motivada tanto pela difusão
destes microcontroladores na indústria e por oferecerem uma arquitetura similar à
RISC, quanto pelo desafio imposto por sua limitação de recursos. Sobre sua arquitetura, esta facilita a geração automática de código, o que mostra-se interessante na
concepção de ferramentas como esta sendo proposta.
Finalmente, dado um programa escrito num subconjunto bem definido da linguagem Lua, a ferramenta desenvolvida, batizada como picoLua, é capaz de gerar
um programa equivalente na linguagem assembly para estes microcontroladores.
Com isso, é possı́vel o desenvolvimento de sistemas embarcados ou mesmo de
aplicações distribuı́das envolvendo uma variedade de plataformas — desde um simples microcontrolador até computadores pessoais, handhelds e servidores de grande
porte. Beneficiando-se da uniformidade e portabilidade da linguagem, é possı́vel ter
uma especificação única do sistema, independentemente do local onde cada parte
é efetivamente implementada. É possı́vel também utilizar as potencialidades oferecidas pela linguagem, como a não diferenciação entre programas e dados, para
realizar migração de código entre os nós do sistema. Pode-se assim ter estratégias
como a carga dinâmica de código, o disparo automático de testes remotamente e
tantas outras.
1.2
Organização deste documento
Este documento descreve o desenvolvimento da ferramenta, apresentando tanto
seus objetivos e o contexto onde é inserida, quanto sua implementação e algumas
aplicações práticas.
No capı́tulo 2, é abordado de uma maneira geral o desenvolvimento de sistemas
8
embarcados, dando ênfase ao desenvolvimento de software. Nele são mostradas
algumas metodologias e possibilidades de projeto.
Já o capı́tulo 3 aborda a linguagem Lua, apresentando suas principais caracterı́sticas e potencialidades, identificando o que a torna interessante para o uso em
sistemas embarcados.
O capı́tulo 4 apresenta o desenvolvimento da ferramenta em si, mostrando seus
princı́pios de funcionamento e identificando suas funcionalidades. São apresentados
também os recursos da linguagem Lua que podem ser mapeadas para os microcontroladores escolhidos.
No capı́tulo 5 são apresentados exemplos de utilização da linguagem Lua e da
ferramenta proposta em sistemas embarcados. São feitas também comparações com
soluções equivalentes, implementadas diretamente em linguagem de montagem.
Finalmente, o capı́tulo 6 traz as conclusões obtidas, fazendo uma análise crı́tica
e apresentando alguns tópicos que poderão ser explorados em trabalhos futuros.
9
2 DESENVOLVIMENTO DE SISTEMAS
EMBARCADOS
Sistemas embarcados podem ser considerados como exemplos de sistemas dedicados ou de aplicação especı́fica. Estão normalmente inseridos em sistemas maiores
e são concebidos para a realização de tarefas especı́ficas, bem conhecidas e determinadas em tempo de projeto. De uma maneira geral, apresentam restrições nos
recursos computacionais disponı́veis, nos custos ou mesmo nas condições de operação. Como conseqüência, diferem-se dos sistemas ditos de propósito geral por
estas peculiaridades.
2.1
Possibilidades de implementação
Do ponto de vista da implementação, os sistemas embarcados apresentam diversas possibilidades, como as listadas a seguir:
• Uso de microprocessadores comerciais, codificando o software em linguagem
de montagem (assembly) ou mesmo em linguagens de mais alto nı́vel, como a
linguagem C e similares;
• Uso de processadores digitais de sinais (DSPs), novamente com software descrito em linguagem de montagem ou, mais recentemente, em linguagens como
C;
• Desenvolvimento de hardware especı́fico, seja através de ASICs ou de ASIPs;
• Uso de FPGAs, acomodando hardware especı́fico, memórias ou mesmo cores
de processadores sintetizáveis;
• Integração em sistemas em um chip (SOC), acomodando CPU, memórias,
conversores, módulos analógicos, etc.
É importante salientar que tais soluções não são necessariamente mutuamente
exclusivas e são muitas vezes encontradas integradas.
2.2
Desenvolvimento de software
Do ponto de vista do software, o desenvolvimento de sistemas embarcados não
difere significativamente do dos demais, com exceção às metodologias que visam
10
Figura 2.1: Fluxo de desenvolvimento de software
o desenvolvimento em conjunto de hardware e software (hardware e software codesign).
Em geral, parte-se de uma especificação geral do sistema e tenta-se delimitar
o que será implementado em software. Busca-se então a prototipação do sistema,
validando-a através de simulações ou mesmo utilizando-a em plataformas reais. Uma
vez validado, o sistema pode ser implementado, desde que neste ponto sejam consideradas as restrições impostas pela plataforma alvo. Isto inclui os recursos disponı́veis,
as restrições temporais, a potência a ser consumida e outras.
Durante este ciclo, diversas ferramentas são utilizadas com fins de simulação,
testes, validação ou mesmo de tradução entre os diversos nı́veis de representação do
sistema. Tradicionalmente, ferramentas como simuladores, compiladores, ligadores
e ferramentas de sı́ntese são utilizadas, como ilustrado na figura 2.1.
2.3
Metodologias alternativas
No contexto de desenvolvimento exposto, notavelmente diversas linguagens de
programação ou de descrição são utilizadas, o que contribui significativamente para
o aumento da complexidade envolvida. Neste sentido, diversas abordagens são propostas visando a simplificação e unificação do processo de desenvolvimento. Uma
atenção especial deve ser dada às alternativas que buscam a integração do desenvolvimento de software e de hardware.
Destaca-se assim a metodologia SASHIMI, que mostra-se interessante por permitir a sı́ntese automática de um ASIP e seu programa a partir de uma descrição
na linguagem Java. Ao utilizar a tecnologia Java, é possı́vel a adoção de suas ferramentas padrão para o desenvolvimento, compilação e validação dos sistemas. Como
conseqüência, tem-se todas vantagens que elas oferecem, como a independência de
plataforma e interoperabilidade. Finalmente, tomam-se os bytecodes gerados para a Máquina Virtual Java (JVM) como ponto de partida para a geração de um
microprocessador dedicado e o respectivo código a ser executado (ITO 2000).
11
Adicionalmente, o processo de codificação pode ser significativamente melhorado
no momento em que são utilizadas ferramentas e linguagens compatı́veis entre si e
com as utilizadas durante a prototipação do sistema. Similarmente, os testes de tais
sistemas, embora não explicitados na figura 2.1, podem ser igualmente beneficiados.
Em geral, tal facilidade é oferecida em ambientes de desenvolvimento proprietários,
especı́ficos para determinadas tecnologias.
Finalmente, sistemas embarcados inseridos em aplicações maiores, como aplicações distribuı́das, com diversos nós de processamento, podem ser beneficiados
diretamente pela migração de código. Por migração de código entende-se a possibilidade de transferir uma determinada tarefa, juntamente com seu contexto de
execução, para que seja realizada remotamente. Tal caracterı́stica pode também ser
útil durante o processo de desenvolvimento e prototipação, pois acelera o ciclo de
codificação-compilação-carga-verificação ao diminuir significativamente o tempo de
carga do software no sistema alvo. Por outro lado, para que isto seja possı́vel, é
necessário existir um núcleo de software no sistema alvo capaz de aceitar conexões
remotas e instanciar as tarefas solicitadas. Várias soluções existem que oferecem tal
recurso, em especial o JavaParty para a linguagem Java (HAU 2003).
12
3
A LINGUAGEM LUA
Conforme exposto anteriormente, a Linguagem Lua foi especialmente concebida
para extensão e customização de aplicações. Assim, uma série de atributos desejáveis para esta classe de linguagens foi levada em conta durante sua implementação (FIG 1994):
• Sintaxe simples, limpa e familiar;
• Implementação e código pequenos, para que o custo de adicioná-la a um sistema hospedeiro seja mı́nimo;
• Expressibilidade e facilidade de descrição de dados, por ser utilizada primariamente como uma linguagem de configuração;
• Extensibilidade adequada, permitindo seu uso em diversos domı́nios e nı́veis
de abstração;
Além disso, a linguagem Lua tem recursos especiais que a tornam poderosa,
extensı́vel e de alto nı́vel, como os listados a seguir. Exemplos de utilização destes
recursos são dados ao final do capı́tulo, na seção 3.4.
• Tabelas associativas, o que permite a implementação de containers de dados
de uma maneira única e ortogonal;
• Habilidade de definir e manipular funções como valores de primeira classe, o
que simplifica significativamente a implementação de facilidades como orientação a objetos;
• Coleta de lixo (garbage collection), eliminando a necessidade de gerência explı́cita de alocação de memória;
• Um mecanismo de extensão da semântica da linguagem através de metatabelas (similar à sobrecarga de operadores em C++).
Por outro lado, como conseqüência de não ter sido planejada para uso em sistemas maiores, foram abolidos alguns mecanismos, como o encapsulamento de dados
(information hiding), a tipagem estática e a ligação incremental de código. Porém,
a ausência de uma tipagem estática, aliada à natureza interpretada da linguagem,
provê uma flexibilidade extra na descrição de dados e na estruturação de software,
o que é dificilmente atingido em linguagens convencionais.
13
3.1
Aplicações
A linguagem Lua tem se destacado em diversas áreas de aplicação, sendo as
principais:
• Em jogos, como engines de inteligência artificial e como “lógica de cola” entre
demais módulos;
• Como CGI, em aplicações Web;
• Como linguagem de configuração de aplicativos e de equipamentos.
3.2
3.2.1
Implementação
Conceituação
Conceitualmente, o suporte à linguagem Lua é disponibilizada como uma pequena biblioteca de funções C que é ligada a uma aplicação hospedeira. A aplicação
instância então ambientes de execução Lua, nos quais são definidos e manipulados
dados através de uma API. É definido também um mecanismo para as aplicações
realizarem chamadas às funções do Lua, seja chamando-as explicitamente através
da API ou interpretando scripts previamente elaborados.
Finalmente, é possı́vel ter funções implementadas pelas aplicações (codificadas
em C, por exemplo) exportadas para o ambiente Lua. Com isto, possibilita-se o
desenvolvimento de bibliotecas de funções para o Lua, implementando-as em outras
linguagens. Estende-se assim a linguagem, oferecendo novas primitivas, otimizando
funcionalidades com desempenho crı́tico ou mesmo provendo acesso a dispositivos
especı́ficos de uma dada arquitetura.
Como exemplo trivial de aplicação que utiliza a linguagem Lua, tem-se o interpretador de comandos Lua. Codificado em C, o interpretador consiste basicamente
em um laço infinito, que lê uma string da entrada padrão e passa-a para a biblioteca
Lua para sua execução. Os resultados da execução — ou as indicações de erro —
são devolvidas para a aplicação que se encarrega de exibi-los na saı́da padrão.
3.2.2
A Máquina Virtual Lua
Do ponto de vista de sua implementação, a linguagem Lua adota uma estratégia
baseada em uma máquina virtual. Tal estratégia é um meio termo entre as adotadas
por linguagens puramente interpretadas e as adotadas por linguagens compiladas.
Por sua vez, a Máquina Virtual Lua (Lua Virtual Machine, ou LVM) utiliza uma
arquitetura baseada em um set de registradores, ao invés da abordagem convencional baseada em pilhas, utilizada por muitos interpretadores. Em especial, o uso
desta abordagem na especificação mais recente da LVM (versão 5.0) resultou em um
aumento de 30% na velocidade de execução (IER 2003).
Esta caracterı́stica da LVM mostra-se também interessante em processos de recompilação de código Lua, especialmente na ferramenta picoLua. Uma vez que
os bytecodes Lua manipulam dados diretamente num conjunto de registradores de
propósito geral, o mapeamento para o modelo de memória dos microcontroladores
escolhidos é imediato.
14
3.3
Utilização
A figura 3.1 mostra um exemplo tı́pico de utilização da linguagem Lua no contexto de desenvolvimento de software. Nele, a linguagem é utilizada como standalone, diferentemente de seu propósito original de utilização embutida em aplicações.
Percebe-se como a linguagem pode ser aproveitada para especificar, prototipar e implementar um sistema a partir de uma descrição única de software.
Figura 3.1: Fluxo de desenvolvimento com a linguagem Lua
3.3.1
Utilização em Sistemas Embarcados
Apesar da linguagem Lua não ter sido originalmente planejada para uso em sistemas embarcados, muitas de suas caracterı́sticas já apresentadas se destacam e
tornam esta possibilidade interessante. A seguir são re-apresentadas algumas caracterı́sticas da linguagem, relacionando-as com as vantagens que podem ser atingidas
em sistemas embarcados.
Sintaxe simples e familiar Diminui a curva de aprendizado, simplifica o processo
de codificação e facilita a manutenção de código;
Implementação compacta e eficiente Permite o uso em sistemas com recursos
limitados, reduzindo custos e energia consumida;
Construções de alto nı́vel Permite expressar conceitos abstratos de uma maneira direta, sem fazer uso de recursos adicionais. Minimiza a possibilidade de
erros de codificação e facilita a manutenção do software;
Facilidade de extensão Permite a utilização em diversos domı́nios: nos sistemas
embarcados em si, durante o processo de desenvolvimento e mesmo nos sistemas maiores que interajam com eles;
15
Especificação e implementação como software livre Oferece uma flexibilidade adicional para se adequar a caracterı́sticas especı́ficas do sistema alvo e
permite a fácil integração com outros módulos e sistemas.
3.4
Exemplos de código
Nesta seção são apresentados alguns trechos de código como exemplo de codificação na linguagem Lua. Com eles, busca-se ilustrar a sintaxe e algumas caracterı́sticas interessantes da linguagem.
A figura 3.2 apresenta um pequeno programa que calcula os números da seqüência
de Fibonacci. Nele, a criação de funções dinamicamente é explorada pela função
cache. Dada uma função f , uma nova função f f é criada e retornada. A função
f f é basicamente a função f original modificada para manter uma tabela com os
resultados de suas execuções anteriores. No caso da função f ib, consegue-se reduzir a
complexidade envolvida, de O(cn ) para O(n), através de um truque de programação
codificado de uma maneira simples e elegante.
-- Funç~
ao de Fibonacci convencional
fib = function( n )
if n<2 then
return n
else
return fib(n-1)+fib(n-2)
end
end
-- Criaç~
ao din^
amica de funç~
oes com memória das
-- últimas execuç~
oes
cache = function( f )
local c = {}
local ff = function( x )
local y = c[x]
if not y then
y = f(x)
c[x] = y
end
return y
end
return ff
end
-- cria-se uma vers~
ao cached para ’fib’, que é
-- executada para comparaç~
ao com a original
cached_fib = cache( fib )
print( fib(N), cached_fib(N) )
Figura 3.2: Cálculo da seqüência de Fibonacci em Lua
A figura 3.3 mostra a definição de algumas estruturas de dados básicas, ilustrando
a implementação de containers dados. Nela, tabelas Lua servem como mecanismo
de implementação.
16
-- tabela como lista: cada entrada
-- ganha um ı́ndice numérico
list = { "Lua", "C", "C++", "Java" }
-- mapas, dicionário ou tabela associativa:
-- para cada chave está associado um valor
map = {}
map["Lua"] = "TeCGraf, PUC-Rio"
map["C++"] = "Bjarne Stroustrup"
map["Java"] = "Sun Microsystems"
-- uso de iteradores para percorrer a tabela
for lang, author in map do
print( lang, "by", author )
end
-- estrutura: cada campo é na verdade
-- uma chave para uma entrada na tabela
picoLua = {
source = "Lua",
implementation = "Lua/LVM",
target = "Assembly/PIC"
}
Figura 3.3: Utilização de tabelas Lua como containers de dados
A figura 3.4 mostra a construção de objetos, dentro do conceito de orientação a
objetos. Graças à não diferenciação entre dados e funções, é possı́vel utilizar tabelas
para armazenar métodos e variáveis membro.
Finalmente é mostrado o mecanismo de extensão da semântica da linguagem
através de meta-tabelas, na figura 3.5. Nela é definido o comportamento do operador
de adição para números complexos.
17
-- ’Circle’ é a funç~
ao construtora, que cria e
-- retorna um objeto cı́rculo
Circle = function( r )
-- um objeto é na verdade uma tabela que
-- guarda seus membros e métodos
local c = {}
c.radius = r
-- self é passado como par^
ametro implicito
-- ao utilizar a sintaxe obj:method( args )
c.area = function( self )
return math.pi * self.radius ^ 2
end
return c
end
-- instanciaç~
ao de um objeto e chamada de método
c = Circle( 10 )
a = c:area()
Figura 3.4: Suporte a orientação a objetos em Lua
18
-- ’Complex’ é o construtor de objetos que
-- represetam números complexos
Complex = function( r, i )
-- o número complexo é uma tabela,
-- com os componentes real e imaginário
local c = {}
c.real = r
c.imag = i
-- ’ops’ é uma tabela com as operaç~
oes
-- sobre números complexos
local ops = {}
-- o nome ’__add’ é predefinido, para
-- implementar a sem^
antica do operador ’+’
ops.__add = function( a, b )
return Complex( a.re+b.re, a.im+b.im )
end
-- finalmente, a tabela ’ops’ é associada à
-- meta-tabela de ’c’, que será consultada
-- pela LVM
setmetatable( c, ops )
end
-- criaç~
ao de complexos
i = Complex( 0, 1 )
x = Complex( 3, 0 )
-- operaç~
ao de soma: chamará o méotodo ’__add’
-- da meta-tabela de ’x’
y = x + i
Figura 3.5: Extensão de semântica na linguagem Lua
19
4
A FERRAMENTA PICOLUA
Este capı́tulo apresenta a ferramenta picoLua, desenvolvida para permitir o uso
da linguagem Lua em microcontroladores e microprocessadores embarcados. Tal
objetivo é atingido fazendo uma recompilação dos programas escritos na linguagem
Lua: a ferramenta é responsável por analisar os bytecodes gerados e por transformálos em programas na linguagem de montagem para a arquitetura alvo escolhida.
Elimina-se assim a necessidade de se ter a Máquina Virtual Lua implementada para
tais plataformas e a conseqüente utilização de um sistema operacional completo.
4.1
O uso da Máquina Virtual Lua
A não utilização da LVM permite o porte da linguagem para sistemas menores.
Mas, por outro lado, a linguagem perde algumas das suas caracterı́sticas marcantes,
como a tipagem dinâmica e o garbage collection. Assim, optou-se por definir um
subconjunto da linguagem suportado pela ferramenta, que delimita tanto a sintaxe
reconhecida quanto a semântica passı́vel de ser mapeada para a arquitetura alvo.
Dentre as caracterı́sticas da linguagem não suportadas diretamente pela ferramenta,
destaca-se a tipagem dinâmica de dados, imprescindı́vel para a diferenciação entre
dados e programas durante a interpretação dos programas. Tal diferenciação é
realizada implicitamente pela LVM em tempo de execução, mas se faz necessária
em tempo de compilação ao se utilizar a ferramenta para a geração de código em
linguagem de montagem.
4.2
Arquiteturas alvo e a geração de código
Como estudo de caso de arquitetura alvo, escolheu-se a famı́lia de microcontroladores PIC da Microchip. Como conseqüência, tem-se a geração de código voltada
para a linguagem de montagem desta arquitetura. Porém, a ferramenta tem sua
implementação modularizada, separando explicitamente as partes responsáveis por
realizar a descompilação, a análise dos bytecodes e geração de código em si. Com
isto, é perfeitamente possı́vel a substituição do módulo de geração de código por
outro, dedicado a diferentes arquiteturas.
Esta possibilidade pode ainda ser levada ao extremo, gerando código em outras
linguagens de alto nı́vel, em especial na linguagem C. Apesar de parecer contraditória, a idéia da utilização de uma ferramenta para tradução de programas escritos
na linguagem Lua para outra linguagem de alto nı́vel é válida por diversos motivos:
• Escrito em Lua, o software se beneficia de um série de as vantagens sobre a
20
linguagem C, como exposto anteriormente;
• A não utilização da máquina virtual e a conseqüente tipagem estática dos
dados pode representar um ganho de desempenho sobre sistemas que utilizem
a linguagem Lua em sua forma interpretada;
• O número de compiladores C disponı́veis para plataformas embarcadas é crescente;
• Muitos compiladores C permitem a otimização de código, explorando possı́veis
paralelismos e caracterı́sticas especiais das arquiteturas alvo;
• A implementação da ferramenta é sensivelmente simplificada pelo fato do compilador C já abstrair detalhes da arquitetura, com destaque para o modelo de
memória a ser seguido;
Adicionalmente, a ferramenta picoLua é implementada quase totalmente na
própria linguagem Lua, o que reforça a versatilidade e expressibilidade da linguagem. Uma exceção, porém, deve ser feita ao módulo que realiza a descompilação
dos bytecodes e que gera uma representação interna do programa sendo analisado.
Tal módulo é implementado em C, uma vez que é inspirado fortemente no código da
Máquina Virtual Lua já existente. Contudo, é perfeitamente viável sua implementação em Lua, com o custo da rescrita do código.
4.3
Implementação
A ferramenta picoLua tem sua implementação dividida em módulos, conforme
ilustrado pela figura 4.1. Nela é exposto também o fluxo de dados sugerido para a
utilização da ferramenta.
Figura 4.1: Estruturação da ferramenta picoLua
21
Basicamente, a ferramenta é composta por três módulos: o nula, que faz a
descompilação dos bytecodes, gerando uma representação interna passı́vel de ser
tratada na linguagem Lua; o analisador de código em si, que identifica as diversas
operações realizadas pelo programa fonte e que chama as primitivas para geração de
código; o backend, ou gerador de código, que é chamado pelo analisador de código
e que gera o código propriamente dito. Finalmente, o código fonte completo da
ferramenta picoLua é apresentado em anexo.
4.3.1
O módulo nula
A tradução de um dado programa com código fonte Lua, de sua forma textual
para sua representação em bytecodes, pode ser realizada pelo compilador Lua, o
luac. No caso da ferramenta, tal tarefa é realizada pela função loadf ile da biblioteca
padrão Lua. O resultado de sua execução é uma nova função que pode ser executada
na Máquina Virtual Lua. Tal função pode ser então atribuı́da a uma variável e
executada posteriormente. No caso da ferramenta picoLua, esta função serve como
entrada para o módulo nula.
Codificado em C, o módulo traduz uma determinada função representada no
formato próprio para execução na LVM para uma representação proprietária, interpretável no ambiente Lua pelos demais módulos da ferramenta. Tal representação,
ilustrada na tabela 4.1, é dada através de uma tabela Lua cujos campos descrevem
o comportamento e as caracterı́sticas da função original. O código executável das
funções, em instruções para a LVM, está presente no campo code desta tabela e
também é tratado pelo nula: cada instrução relacionada no código executável é
representada conforme a estrutura apresentada na tabela 4.2.
Tabela 4.1: Representação de uma função Lua gerada pelo nula
Campo
Descrição
is main
Flag que indica se a função corresponde
à função “main” do programa
source
String com o nome do arquivo fonte original
line def ined Linha na qual a função é definida
num param Número de parâmetros recebidos pela função
is vararg
Flag que indica se a função recebe um número
variável de argumentos
stack size
Número máximo de registradores utilizados
pela função
code
Tabela com o código da função, em uma
representação interna para os bytecodes
constants
Tabela com as constantes definidas na função
locals
Tabela com as variáveis locais definidas
na função
upvalues
Tabela com as upvalues utilizadas pela
função (variáveis locais de uma função pai,
dentro da qual a função em questão é definida)
f unctions
Tabela com protótipos de funções, definidas
dentro da função em questão
22
Tabela 4.2: Representação de uma instrução gerada pelo nula
Campo Descrição
opcode
Opcode da instrução utilizado pela LVM
a
Operando a utilizado pela instrução
b
Operando b
c
Operando c
b
Operando b
sbx
Operando sbx
line
Linha onde a instrução é definida
4.3.2
O analisador de código
Dada a representação de uma função Lua, utilizável como dado por outra função
Lua, é possı́vel prever estaticamente seu funcionamento e algumas de suas caracterı́sticas de execução. Na ferramenta picoLua, tal tarefa é realizada pelo analisador de código, responsável por identificar operações como atribuição de variáveis,
operações aritméticas, testes, manipulação de tabelas, etc, chamando o gerador de
código em si. Porém, apenas um subconjunto da linguagem é suportado e conseqüentemente algumas construções não são suportadas pela ferramenta. Novamente, cabe
ao analisador de código identificá-las e reportar ao usuário a impossibilidade de
utilizá-las.
De posse da especificação das instruções suportadas pela LVM, é definido precisamente o escopo de atuação do analisador de código. Em um primeiro momento, a
tradução é feita tomando-se instrução por instrução, sem que o contexto em que elas
são inseridas seja levado em consideração. Em um segundo momento, são tratadas
as construções parcialmente suportadas pela ferramenta e que eventualmente têm
suas semânticas alteradas para acomodar as restrições impostas pela arquitetura
alvo escolhida. Neste grupo estão inseridas a manipulação de tabelas, a criação de
funções e o acesso a variáveis globais.
A seguir é dada uma relação completa dos opcodes definidos pela LVM e que caracterizam as classes de instruções gerados pelo Lua. É dada também uma pequena
descrição para cada opcode, juntamente com alguns comentários sobre a abordagem
adotada pela ferramenta para tratá-lo — ou não — e traduzi-lo corretamente para
a arquitetura alvo.
MOVE Move dados entre registradores. Sua implementação pela ferramenta é
imediata, pois corresponde ao modelo de memória adotado pelos microcontroladores utilizados.
LOADK Atribui uma constante ao um dado registrador; implementado pela ferramenta como carga de um valor literal a um registrador.
LOADBOOL Similar à carga de uma constante, com a diferença de que um salto
condicional adicional e opcional é especificado. Tal salto é utilizado para
permitir a utilização de duas instruções consecutivas com LOADBOOL. É
implementado como um salto convencional ou simplesmente omitido, pois a
condição verificada é conhecida em tempo de compilação.
23
LOADNIL Também similar à carga de uma constante, mas modificado pois o
conceito de valor nulo, utilizado pela LVM, é redefinido para ser representado
como um valor puramente numérico na arquitetura alvo.
GETUPVAL, SETUPVAL Obtém ou atribui o valor de uma upvalue (variável
local definida em uma função pai da função sendo analisada). Não é suportado
pela ferramenta uma vez que o suporte a criação de funções dinamicamente
dado pela ferramenta não abrange a especialização de funções.
GETGLOBAL, SETGLOBAL Obtém ou atribui o valor de uma variável global,
transferindo-a de/para um registrador. Sua implementação é razoavelmente
imediata, pois deve-se manter uma relação de variáveis globais a serem alocadas pela ferramenta. Adicionalmente, um cuidado especial deve ser tomado
pois chamadas a funções (por instruções CALL e TAILCALL) acabam sendo
codificadas utilizando estas instruções para recuperar uma referência ao corpo
da função e armazená-la em um registrador. Tal ação deve ser mapeada para
uma chamada de função convencional, do ponto de vista da plataforma a ter
seu código gerado pela ferramenta.
GETTABLE, SETTABLE Obtém ou define um campo de uma tabela, dado um
ı́ndice ou uma chave. Não é suportada pela ferramenta. Porém, às extensões
definidas pela ferramenta para acomodar o acesso direto aos recursos da plataforma acabam sendo codificadas com tais opcodes. Assim, uma análise criteriosa dos argumentos é feita para identificar tais funcionalidades e tratá-las
da maneira correta, como exposto na seção 4.3.4.
NEWTABLE Cria uma nova tabela e armazena-a em um registrador. Não suportado pela ferramenta, devido à pequena capacidade de armazenamento da
plataforma escolhida;
SELF Similar a GETTABLE, com a diferença que a referência à tabela é mantida
na pilha para ser passada como parâmetro a uma função, implementando
assim, dentro dos conceitos da orientação à objetos, uma chamada de método
de um objeto. Não é suportado pela ferramenta em sua versão atual.
ADD, SUB, UNM, NOT Operações aritméticas, que operam diretamente sobre
registradores ou constantes. O mapeamento para instruções da linguagem de
montagem alvo é imediato, pelos mesmos motivos apresentados no opcode
MOVE.
MUL, DIV, POW Também operações aritméticas, mas não suportadas diretamente pela arquitetura. Conseqüentemente não são suportadas pela ferramenta.
CONCAT Concatenação de registradores, com uso especı́fico para strings. Como o
suporte a strings não está previsto pela ferramenta, tal opcode não é suportado.
JMP Salto relativo incondicional. Faz referência à numeração interna das instruções da função e assim o deslocamento deve ser recalculado pois não há
uma correspondência direta entre o número de instruções Lua e da arquitetura alvo.
24
EQ, LT, LE, TEST Testes e comparações entre dois registradores ou constantes,
seguido de um salto condicional. Também tem um mapeamento imediato,
visto que a arquitetura prevê comparações similares.
CALL, TAILCALL Chamadas de funções Lua. Representam o maior desafio imposto à codificação da ferramenta, uma vez que as funções a serem chamadas
não são explicitadas nas instruções. As funções são referenciadas a partir de
registradores, previamente preenchidos. Assim, para fins de simplificação, apenas funções definidas no escopo global podem ser chamadas, fazendo com que
as referências presentes nos registradores sejam obtidas a partir de variáveis
globais. Finalmente um mecanismo especial para tratamento a chamadas de
funções é realizado em conjunto com o opcode GETGLOBAL.
RETURN Retorno de uma função, traduzido para uma instrução similar na geração de código.
FORLOOP Iteração em um laço f or numérico. Tem uma semântica associada
sofisticada, uma vez que uma série de atribuições e testes são definidos para
as instruções. Seu tratamento é simples, pois tais operações são diretamente
tratadas pela geração de código.
TFORLOOP, TFORPREP Iteração em um laço f or percorrendo elementos de
uma tabela. Não suportados, dado que tabelas não o são.
SETLIST, SETLISTO Atribuição e inicialização de tabelas, tratando-as como
listas. Novamente, não suportados.
CLOSE Finaliza o contexto de execução em questão, especificamente o set de registradores. É simplesmente ignorado pela ferramenta, pois ao contrário da LVM,
o set de registradores da arquitetura alvo é fixo e não pode ser redimensionado.
CLOSURE Instancia (cria) uma nova função a partir de um dado protótipo précompilado. Tal operação é utilizada pois funções podem ser criadas dinamicamente e eventualmente acessam upvalues que devem ser determinadas em
tempo de execução. Como resultado, tem-se uma referência à função criada em um registrador. Assim, geralmente instruções desta classe são seguidas por instruções que armazenam a referência em variáveis ou em tabelas.
Na ferramenta, tais instruções são utilizadas internamente para associar os
protótipos de funções aos nomes de funções utilizados no restante do código,
preenchendo-os na tabela de sı́mbolos. Tal esquema é processado em conjunto
com o tratamento às instruções com o opcode SETGLOBAL.
4.3.3
O gerador de código para PIC
Definido o conjunto de instruções e funcionalidades da linguagem Lua tratadas
pela ferramenta, a geração de código é realizada pelo chamado backend. Mais do que
a geração de código, o backend é responsável pelo suporte à arquitetura alvo como
um todo. Isto envolve a correta utilização de seu modelo de memória, a definição de
polı́ticas para alocação de recursos (no caso de registradores e de variáveis globais)
e para passagem e retorno de parâmetros a funções.
Quanto ao modelo de memória, a famı́lia de microcontroladores PIC oferece
apenas um espaço de endereçamento de dados, visto pelo software como um set
25
registradores para uso geral. Este acaba por ser utilizado pela ferramenta para
acomodar tanto o set de registradores da LVM, quanto as variáveis globais e ainda
uma pilha mantida em software para passagem de parâmetros. Foi então definida a
polı́tica para alocação de registradores:
• As posições de memória iniciais são utilizadas para acomodar as variáveis
globais. Uma vez que este número é determinado em tempo de compilação,
tem-se de antemão o tamanho em memória por elas ocupado.
• As posições seguintes são reservadas para acomodar o set de registradores
utilizado pela Máquina Virtual Lua. Como o tamanho do set de registradores
é variável e dependente da função em execução, seu limite não é definido e tem
seu espaço compartilhado com a pilha definida em seqüência.
• As últimas posições de memória são utilizadas para acomodar uma pilha mantida em software para passagem de parâmetros e salvamento de contexto entre
chamadas de funções. Sendo uma pilha, tal região tem seu tamanho modificado dinamicamente e compartilhado com o espaço reservado para o set de
comandos.
A figura 4.2 ilustra esta polı́tica de alocação, mostrando valores reais de posições
de memória para o PIC16F876.
Figura 4.2: Mapa de utilização de memória no PIC
Finalmente, é definida a polı́tica de passagem de parâmetros entre funções e
de salvamento de contexto. Tal esquema segue o modelo clássico de passagem de
parâmetros por pilha, o que permite a definição de funções reentrantes e mesmo
recursivas. Adicionalmente, o contexto de execução de uma função (do ponto de
vista do Lua) é seu set de registradores. Assim,prólogos e epı́logos são adicionados
aos códigos gerados para as funções, tratando o salvamento e a recuperação dos
registradores utilizados de uma maneira transparente.
4.3.4
Extensões à linguagem Lua
A linguagem Lua define uma série de operações e funcionalidades para manipulação de dados. Tais dados são manipulados sobre um set de registradores genéricos,
que acabam sendo mapeados automaticamente para o set de registradores do microcontrolador. Tal polı́tica é limitada, uma vez que impede que dados sejam obtidos
26
e gerados para o meio externo. Assim, foi definido um mecanismo para acesso direto ao hardware do microcontrolador. Pode-se então manipular diretamente suas
portas de entrada e saı́da, além de seus registradores especiais para configuração e
manipulação de periféricos.
Do ponto de vista dos programas em escritos em Lua, uma tabela global regs
é disponibilizada, cujos elementos correspondem exatamente aos registradores internos do microcontrolador. Adicionalmente, cada registrador pode ter seus bits
manipulados individualmente. Tratando-os como tabela, seus bits são acessı́veis
tanto por mnemônicos como por seus ı́ndices. O trecho de código presente na figura
4.3 ilustra um pouco das possibilidades de sua utilização.
__regs.PORTA
= 0xFF
__regs.PORTB[7] = 0
-- seta todos os bits da porta A
-- limpa o bit 7 da porta B
saved_rcif = __regs.PIR1.RCIF
-- guarda a flag de interrupç~
ao
Figura 4.3: Exemplo de uso da tabela
regs
Além disso, um mecanismo para codificação de instruções em linguagem de montagem, embutidas em código fonte Lua também é previsto. A função especial asm
recebe como argumento uma string que é copiada literalmente no código gerado, para
uso exclusivo na ferramenta de montagem. Pode-se assim ter trechos com desempenho crı́tico codificados diretamente em assembly. Tem-se ainda a possibilidade de
utilização de instruções especiais da arquitetura, não sintetizáveis pela ferramenta
picoLua. Um exemplo de utilização desta funcionalidade é mostrada na figura 4.4.
-------
Divisao por dois, implementada com um rotate-right.
O assembly inline é necessário para emitir a instruç~
ao
’rrf’, n~
ao gerada pelo picoLua.
Os argumentos das funç~
oes s~
ao mapeados automaticamente para
os registradores R0, R1, R2, ... Sabendo-se isso, pode-se
manipular seus valores diretamente em assembly!
divide_by_2 = function( x )
__asm__( "rrf R0, f" )
return x
end
-- Coloca o microcontrolador em modo standby, através
-- da instruç~
ao ’sleep’ gerada por assembler inline
sleep = function()
__asm__( "sleep" )
end
Figura 4.4: Exemplo de uso de assembly inline
Tem-se ainda a possibilidade de passar parâmetros para o montador que processará os dados gerados pela ferramenta picoLua. Tais parâmetros envolvem o exato
modelo do microprocessador a ser utilizado, bem como sua palavra de configuração.
O modelo é importante também para definir a polı́tica de alocação de memória. Já a
27
palavra de configuração traz informações utilizadas pelo montador ao gerar imagens
para programação dos microcontroladores.
Para acomodar tais informações, foram definidas as funções reservadas target
e conf ig , que recebem como parâmetro, respectivamente, uma string que define
o modelo do microcontrolador e a palavra de configuração a ser embutida na imagem
para programação. A figura 4.5 traz exemplos de utilização de tais funcionalidades
na ferramenta picoLua.
__target__( "p16f876" )
__config__( "_HS_OSC
& _WDT_OFF
& _PWRTE_ON &"
"_BODEN_ON
& _LVP_OFF
& _CPD_OFF &"
"_WRT_ENABLE_OFF & _DEBUG_OFF & _CP_OFF
" )
Figura 4.5: Exemplo de definição do target e da palavra de configuração
Por fim, permite-se a associação de uma função ao tratamento de interrupções.
Tal facilidade é novamente oferecida através de uma função especial, no caso a
função interrupt . Tal função recebe como parâmetro o nome da função que
efetivamente tratará as interrupções e se encarrega de gerar o código de entrada
e saı́da de interrupção. O salvamento de contexto não é implementado, ficando a
cargo da aplicação lidar com tais detalhes. Exemplos de utilização deste recurso são
as aplicações que utilizam o módulo de comunicação serial, apresentadas no capı́tulo
5.
4.4
Metodologia proposta
A ferramenta proposta tem uso ilustrado pela figura 4.6. Percebe-se a possibilidade de utilização da linguagem Lua durante a fase de desenvolvimento e prototipação
do software, através de um ambiente de simulação em um computador pessoal, por
exemplo. A mesma descrição pode então, uma vez simulada e validada, ser utilizada
como entrada da ferramenta picoLua, gerando assim o código final a ser programado
no sistema alvo. É ilustrado também a possibilidade de se ter o alvo da ferramenta
redefinido, gerando código em linguagem C, por exemplo.
Finalmente, é apresentada uma comparação entre a metodologia proposta através
da ferramenta picoLua e a metodologia SASHIMI. A tabela 4.3 mostra algumas de
suas caracterı́sticas, principalmente no que diz respeitos às arquiteturas, linguagens
e ambientes envolvidos.
Tabela 4.3: Comparação entre as metodologias picoLua e SASHIMI
Caracterı́stica
picoLua
SASHIMI
Linguagem
Lua
Java
Ambiente de execução
LVM
JVM
Aplicação
SW embarcado
HW/SW embarcados
Ambiente de desenvolvimento
JDK
—
Entrada
bytecodes Lua
bytecodes Java
Saı́da
assembly PIC ASIP femtoJava (VHDL)
+ programa
28
Figura 4.6: Metodologia proposta com a ferramenta picoLua
29
5
ESTUDOS DE CASO
Este capı́tulo apresenta algumas aplicações que ilustram o uso da ferramenta
picoLua e da linguagem Lua em sistemas embarcados. Em um primeiro momento,
a ferramenta picoLua é utilizada para a codificação isolada de funções básicas para
o microcontrolador PIC na linguagem Lua. Tais funções permitem comparações diretas com implementações equivalentes em linguagem assembly, o que é interessante
para avaliar a eficiência da geração de código pela ferramenta.
Em um segundo momento, a ferramenta é utilizada para codificar o software de
um sistema de aquisição de dados, com comunicação em tempo real com um computador pessoal. São explorados então alguns conceitos da engenharia de software,
como a modularização e a reutilização de código.
Finalmente, é apresentado um modelo para desenvolvimento de aplicações distribuı́das, onde a linguagem Lua é utilizada para descrever o software de seus diversos
componentes. Nele podem ser exploradas algumas das caracterı́sticas interessantes
da linguagem, como a migração de código e a utilização da mesma em múltiplas
plataformas.
5.1
Multiplicação de inteiros
Neste exemplo é realizada a multiplicação de dois números inteiros. Tais números
são representados por variáveis sob o ponto de vista da linguagem Lua, mas que são
mapeados automaticamente para registradores de 8 bits do microprocessador. O
resultado obtido é armazenado em outra variável, cuja representação também é
dada em 8 bits. Neste sentido, nenhum controle de overflow é realizado. O exemplo
é útil por explorar a codificação de operações aritméticas e de laços em Lua. A
figura 5.1 apresenta o programa elaborado em suas três representações tratadas pela
ferramenta: a primeira coluna contém o programa original, codificado na linguagem
Lua; a segunda coluna, os bytecodes gerados pelo compilador Lua, identificando o
opcode e seus operandos; finalmente, a terceira coluna mostra o código assembly
gerado, resultado da recompilação dos bytecodes pela ferramenta picloLua.
Com este exemplo, percebe-se nitidamente o efeito do tratamento individualizado de instruções por parte da ferramenta. Uma vez que o contexto em que uma
instrução está inserida não é levado em consideração, é definida uma correspondência
um-para-um entre uma dada instrução da LVM e o código gerado pela ferramenta. O que por um lado simplifica a implementação da ferramenta, por outro leva
à geração de código ineficiente, passı́vel de ser otimizado. Isto pode ser percebido
nos casos onde valores são movidos do registrador de trabalho do microprocessador
(registrador w) para registradores de uso geral (R0, R1, ...) e na instrução seguinte
30
Código Lua original
Bytecodes Lua (LVM)
Assembly PIC
--------------------------------------------------------------------------------local a, b
LOADNIL
0, 1, 0
clrf R0
clrf R1
local res = 0
LOADK
2, 2, 0
movlw 0x00
movwf R2
for bit = 0,7 do
LOADK
3, 2, 0
movlw 0x00
movwf R3
LOADK
4, 3, 7
movlw 0x07
movwf R4
LOADK
5, 4, 1
movlw 0x01
movwf R5
SUB
3, 3, 5
movf R3, w
subwf R5, w
movwf R3
JMP
0, 12
goto L0P22
__asm__( "rrf R0, f" )
GETGLOBAL 6, 5
L0P10:
LOADK
7, 6
CALL
6, 2,
1
rrf
R0, f
if __regs.STATUS.C then
GETGLOBAL 6, 7
movf STATUS, w
GETTABLE
6, 6, 258
movwf R6
GETTABLE
6, 6, 259
clrf R6
btfsc STATUS, C
comf R6, f
TEST
6, 6
0
movf R6, w
btfsc STATUS, Z
JMP
0, 1
goto L0P19
res = res + b
ADD
2, 2,
1
movf R2, w
addwf R1, w
movwf R2
end
__asm__( "rlf R1, f"
)
GETGLOBAL 6, 5
LOADK
7, 10
CALL
6, 2,
1
rlf
R1, f
FORLOOP
3,-13
L0P22: movf R5, w
addwf R3, f
movf R3, w
subwf R4, w
btfsc STATUS, C
goto L0P10
end
RETURN
0, 1,
0
return
Figura 5.1: Codificações em Lua, bytecodes Lua e assembly do PIC
31
são trazidos novamente para o registrador de trabalho. Obviamente, tal overhead
não é gerado codificando-se diretamente na linguagem de montagem, mas, por outro lado, pode ser reduzido drasticamente através de ferramentas de otimização de
código automatizadas ou pela análise semântica durante o processo de geração de
código.
Por fim, o mesmo programa foi codificado diretamente em assembly e teve seu
tamanho avaliado para fins de comparação com o código gerado pela ferramenta. O
resultado desta comparação é apresentado na seção 5.5, juntamente com as comparações dos demais programas de teste.
5.2
Comunicação serial
Este exemplo configura e utiliza o módulo de comunicação serial (USART) presente no microcontrolador, para realizar um loop local em uma comunicação com um
microcomputador pessoal. São utilizadas as funcionalidades da ferramenta picoLua
para acesso direto ao hardware e aos registradores do PIC. Novamente, é feita uma
comparação com o mesmo sendo descrito diretamente em assembly. Os resultados
são também apresentados na seção 5.5.
5.3
Sistema de medição de vibração
O Sistema de Medição de Vibração com Acelerômetros de Estado Sólido, desenvolvido no Laboratório de Processamento de Sinais e Imagens, LaPSI, da Universidade Federal do Rio Grande do Sul, é um sistema de aquisição de dados de
aceleração obtidos através de acelerômetros de estado sólido. O sistema é capaz de
transmiti-los em tempo real para um computador PC, onde a comunicação pode
ser feita via interface serial ou paralela. O sistema, batizado de AXPC, tem seu
uso ilustrado na figura 5.2, onde é inserido em um sistema tı́pico de medição de
vibração (SOC 2002).
Figura 5.2: Sistema tı́pico de medição de vibração
O AXPC faz uso de um microntrolador PIC e tem seu firmware originalmente codificado na linguagem assembly. Seu firmware foi então reescrito na linguagem Lua
com a ferramenta picoLua. Assim, foi explorada a modularização de código através
da criação de módulos independentes para configuração e inicialização de dispositivos, para atendimento a interrupções e para recebimento e transmissão de dados.
32
A possibilidade de reuso de código foi validada utilizando as funções previamente
criadas para o programa de exemplo exposto na seção 5.2.
O código fonte para o firmware do AXPC escrito em Lua é apresentado em anexo
e serve como exemplo completo da utilização da ferramenta. São explorados praticamente todo os recursos suportados da linguagem, além das construções especı́ficas
da ferramenta picoLua para utilização nos microcontroladores PIC.
5.4
Modelo para aplicações distribuı́das
Finalmente, é apresentado um modelo para desenvolvido de aplicações distribuı́das que envolvam diferentes arquiteturas computacionais e que façam uso da
linguagem Lua. Apesar de extremamente simples, o modelo torna-se interessante por ser baseado em uma rede heterogênea e por demonstrar a versatilidade da
linguagem.
5.4.1
Plataformas utilizadas
As plataformas utilizadas são normalmente associadas à domı́nios de aplicações
diferentes, o que permite a construção de um sistema significativo do ponto de vista
da utilização da linguagem. São elas:
• Computador pessoal (PC) com o sistema operacional Linux. A linguagem
Lua é utilizada em sua implementação tradicional, baseado na sua máquina
virtual. São utilizadas suas bibliotecas padrão, com algumas modificações para
acomodar o suporte a sockets TCP/IP (NEH 2003) e à comunicação serial.
Tais facilidades são utilizadas para comunicação com os demais dispositivos
envolvidos na aplicação.
• Handheld iPAQ, com o sistema operacional PocketPC (originalmente conhecido como Microsoft WindowsCE). O suporte à linguagem Lua é dado através
do porte da linguagem e da máquina virtual (BLA 2003), especialmente modificadas para utilizarem a API deste sistema operacional (Win32 API). É
acrescentado o suporte a sockets TCP/IP e à interface gráfica com o usuário
(GUI). É conectado ao PC via interface USB, sobre a qual é definida uma
interface PPP para transporte de TCP/IP;
• Kit microcontrolado (PIC16F876), onde a ferramenta picoLua é utilizada como
suporte à linguagem Lua. A interface serial é utilizada para comunicação com
o PC e, indiretamente, com o iPAQ.
5.4.2
Conectividade
A comunicação entre os elementos do sistema é dada quase integralmente através
de sockets. A exceção é dada ao PIC, que não possui uma implementação da pilha
TCP/IP. Assim, para tornar a comunicação entre os nós perfeitamente simétrica,
foi criado um pequeno daemon que, executado no PC, faz o intermédio entre a
interface serial para comunicação com o PIC e um socket para comunicação com
os demais elementos. Por fim, tem-se uma rede que, embora heterogênea, tem seus
elementos acessı́veis por uma maneira única. A figura figura 5.3 apresenta tal modelo
de comunicação.
33
Figura 5.3: Arquitetura para aplicações distribuı́das
5.4.3
Benefı́cios
Ao se utilizar um modelo como o proposto para aplicações distribuı́das, uma
série de benefı́cios são atingidos, além das vantagens já mostradas na seção 3.3.1.
Pode-se citar:
Metodologia de projeto única Por se tratar de uma linguagem com implementações para diversas plataformas, tem-se possibilidade de seguir uma metodologia de projeto única, compartilhando ferramentas de projeto, descrições de
sistemas e mesmo bibliotecas e código-fonte;
Descrição única do sistema No momento em que todos os nós são codificados na
mesma linguagem e seguem a mesma metodologia de projeto, pode-se utilizar
uma descrição única do sistema. Pode-se decidir, em tempo de projeto ou
mesmo de execução, o que será executado em cada nó, sem a necessidade de
se ter implementações diferentes para as diferentes arquiteturas envolvidas;
Migração de código/carga dinâmica de aplicações Pode-se utilizar a migração
de código para implementar abordagens como a carga dinâmica de programas.
Como conseqüência, tem-se a possibilidade de se implementar arquiteturas reconfiguráveis ou mesmo de reduzir o as necessidades de memória dos elementos
do sistema, tornando-os simples agentes que recebam e executem programas
remotamente.
5.5
Resultados
Com o propósito de avaliar o código gerado pela ferramenta e comparar seu desempenho com soluções equivalentes, descritas diretamente em assembly, alguns programas de exemplo foram escolhidos. O tamanho, medido em palavras da memória
de programa, foi escolhido como critério para a avaliação de tais programas. Esta
métrica, além de avaliar a eficiência na geração de código por parte da ferramenta,
é válida também para avaliar o tempo de execução dos programas. Isto porque a
especificação do microcontrolador define o tempo de execução por instrução como
sendo único e conhecido em tempo de desenvolvimento.
34
Por outro lado, foi avaliado também o número de linhas dos programas fonte
originais. Isto é útil para quantificar e ilustrar as vantagens da utilização de uma
linguagem como Lua em sistemas embarcados. Percebe-se nitidamente uma redução
no número de linhas de código escritas, o tem que como conseqüência a simplificação
do processo de desenvolvimento.
Os resultados obtidos são mostrados na tabela 5.1, juntamente com estimativas
para as possı́veis otimizações sobre os programas gerados pela ferramenta picoLua.
Esta estimativa é baseada em otimizações manuais, realizadas sobre o código assembly gerado pela ferramenta.
Tabela 5.1: Comparação entre implementações em Lua e Assembly
Programa
Multiplicação inteiros
Comunicação serial
Firmware AXPC
Linhas de código
Assembly Lua
17
11
102
55
283
157
Código gerado (palavras de memória)
Assembly Lua
Lua otimizado
17
34
29
80
179
133
174
482
251
35
6
CONCLUSÃO
Este trabalho propõe o uso da linguagem Lua em sistemas embarcados. Para isto
foi desenvolvida a ferramenta picoLua, que possibilita o uso da linguagem Lua em
microcontroladores através da recompilação de bytecodes. A ferramenta mostrouse interessante por fazer a ligação entre dois domı́nios distintos: o dos sistemas
embarcados, utilizando processadores simples, com restrições de recursos e de projeto
bem definidas, com o das linguagens de scripting de alto nı́vel, tradicionalmente
interpretadas e utilizadas em aplicações maiores. Adicionalmente, a linguagem Lua
foi explorada em outros sistemas, já com recursos adicionais que permitam o uso de
um sistema operacional.
A prototipação de sistemas com a linguagem Lua também foi visada com este
trabalho. Ao tratar o desenvolvimento dos programas de exemplo, foram postas
em prática algumas idéias como a modularização e o reuso de código, importantes
para acelerar o processo de desenvolvimento de software. Além disso, a utilização
de um código fonte único, com partes sendo implementadas na plataforma de desenvolvimento e aos poucos sendo migradas para a plataforma alvo mostrou-se uma
possibilidade interessante oferecida pela linguagem Lua.
Embora pouco explorado neste trabalho, o teste de sistemas embarcados pode
ser diretamente beneficiado pelo uso da linguagem Lua. Com a flexibilidade de
descrição de dados e de integração de módulos, a linguagem Lua mostra-se uma
alternativa para a elaboração de estratégias de teste. Parte-se de uma descrição única
e centralizada dos testes a serem realizados no sistema como um todo, para então
dispará-los remotamente, possivelmente em plataformas variadas. O modelo para
desenvolvimento de aplicações distribuı́das, apresentado como exemplo de utilização
da linguagem e da ferramenta, apresenta esta idéia ao permitir a migração de código
entre os nós, eliminando a necessidade de se ter os testes previamente codificados e
armazenados em todos os dispositivos envolvidos.
Do ponto de vista de sua implementação, a ferramenta picoLua é igualmente
interessante por ser codificada na própria linguagem Lua. Utiliza os programas em
Lua já compilados em bytecodes, o que dispensa a implementação de analisadores
léxico e sintático, já tratados pela linguagem em sua forma tradicional. Com isto,
simplificou-se o desenvolvimento da ferramenta, pois os analisadores representam
grande parte da complexidade de um compilador convencional. Permitiu-se ainda
focar os esforços na geração de código e nas extensões à linguagem para sua utilização
em sistemas embarcados.
Por outro lado, a ferramenta ainda é um trabalho em andamento. Há muito a
ser feito para torná-la uma alternativa concreta para o desenvolvimento de sistemas
reais perfeitamente utilizável. O tratamento e a verificação de erros são deficitários,
36
embora já seja dada uma atenção especial aos erros de sintaxe por parte das ferramentas padrão da linguagem Lua. Além disso, o código gerado é ineficiente e
muitas das construções significativas da linguagem Lua não são suportadas, como o
uso de meta-tabelas para extensões de semântica, o próprio uso de tabelas e o tratamento de funções como dados. Há também a possibilidade de geração de código
para outras plataformas que não a famı́lia de microcontroladores PIC atualmente
suportada. Deixa-se então tais melhorias como sugestões para trabalhos futuros,
juntamente com a idéia da utilização da linguagem e da ferramenta em cores como
o RISCO ou os da famı́lia ARM.
37
REFERÊNCIAS
[FIG 1996] FIGUEIREDO, L; IERUSALIMSCHY, R; CELES, W. Lua: an extensible embedded language. In: Dr. Dobb’s Journal 21 #12 (Dec
1996). p.26-33.
[FIG 1994] FIGUEIREDO, L; IERUSALIMSCHY, R; CELES, W. The design
and implementation of a language for extending applications.
In: Proceedings of XXI Brazilian Seminar on Software and Hardware
(1994) 273-283.
[STOL 2003] STOL, K. Pirate: A Compiler for Lua targeting the Parrot
Virtual Machine. Hamburg, 2003. 96p.
[ITO 2000] ITO, Sergio. Projeto de Aplicações Especı́ficas com Microcontroladores Java Dedicados. Porto Alegre: PPGC da UFRGS, 2000.
84p.
[HAU 2003] HAUMACHER, B. et al. Transparent Distributed Threads for
Java. In: Proceedings of the 5th International Workshop on Java for
Parallel and Distributed Computing (IPDPS 2003). Nice: France, IEEE
Computer Society, 2003.
[IER 2003] IERUSALIMSCHY, R. The Virtual Machine of Lua In: Lightweight
Languages 2003 (LL3). Cambridge: MIT, 2003.
[SOC 2002] SOCAL, F. Sistema de Medição de Vibração com Acelerômetros
de Estado Sólido. Porto Alegre: LaPSI-DELET-UFRGS, 2002. 14p.
[NEH 2003] NEHAB, D. LuaSocket:
IPv4 Sockets support for the
Lua language. Disponı́vel em http://www.tecgraf.puc-rio.br/ diego/luasocket/new/. Acesso em: dez. 2003.
[BLA 2003] BLANDING, S. Lua on the PocketPC. Disponı́vel
http://www.uoam.net/lua.html. Acesso em: dez. 2003.
em
38
APÊNDICES
Código fonte da ferramenta picoLua
Código fonte do firmware do AXPC

Documentos relacionados