Está en la página 1de 175

ndice general

1. Introduccin 5
1.1. Estructura de la memoria . . . . . . . . . . . . . . . . . . . . . . . . . 7
2. Los microcontroladores PIC 9
2.1. Qu es un microcontrolador? . . . . . . . . . . . . . . . . . . . . . . . 9
2.2. Aplicaciones . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 10
2.3. Historia de los microcontroladores PIC . . . . . . . . . . . . . . . . . . 10
2.4. Las gamas de PIC . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 11
2.5. Los PIC de gama alta . . . . . . . . . . . . . . . . . . . . . . . . . . . . 11
2.6. El PIC18F4550 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 12
3. Arquitectura PIC 13
3.1. Diseo del PIC . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 13
3.2. CPU . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 14
3.3. ALU . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 14
3.4. Organizacin de la memoria . . . . . . . . . . . . . . . . . . . . . . . . 15
3.4.1. Memoria de programa . . . . . . . . . . . . . . . . . . . . . . . 15
3.4.2. Memoria de datos . . . . . . . . . . . . . . . . . . . . . . . . . 17
3.5. Juego de instrucciones . . . . . . . . . . . . . . . . . . . . . . . . . . . 21
3.6. Perifricos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 30
3.6.1. Puertos de Entrada/Salida . . . . . . . . . . . . . . . . . . . . 30
3.6.2. Universal Serial Bus . . . . . . . . . . . . . . . . . . . . . . . . 32
3.6.3. SPP . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 32
3.6.4. MSSP . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 33
3.6.5. EUSART . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 33
3.6.6. Memoria EEPROM . . . . . . . . . . . . . . . . . . . . . . . . . 35
3.6.7. Memoria Flash . . . . . . . . . . . . . . . . . . . . . . . . . . . 39
3.6.8. Timers . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 44
3.6.9. Capturador, comparador y modulador de ancho pulso . . . . 46
3.6.10. Conversor A/D . . . . . . . . . . . . . . . . . . . . . . . . . . . 47
1
Compilador de C para el microcontrolador Microchip PIC18F4550 2
3.6.11. Comparador Analgico . . . . . . . . . . . . . . . . . . . . . . 49
3.6.12. Mdulo de Tensin de Referencia . . . . . . . . . . . . . . . . 50
3.6.13. Otros . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 50
3.7. Caractersticas especiales de la CPU . . . . . . . . . . . . . . . . . . . 51
3.7.1. Interrupciones . . . . . . . . . . . . . . . . . . . . . . . . . . . . 51
3.7.2. Perro Guardin . . . . . . . . . . . . . . . . . . . . . . . . . . . 53
3.7.3. Modo de reposo . . . . . . . . . . . . . . . . . . . . . . . . . . . 53
3.7.4. ICSP . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 53
3.8. Caractersticas elctricas . . . . . . . . . . . . . . . . . . . . . . . . . . 53
4. GNU Compiler Collection 56
4.1. Historia de GCC . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 56
4.2. Caractersticas . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 57
4.3. Estructura . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 58
4.3.1. Front-end . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 58
4.3.2. Middle-end . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 59
4.3.3. Back-end . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 59
5. Diseo de la solucin 60
5.1. Registros . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 60
5.2. Memoria . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 61
5.3. Paso de argumentos a funciones . . . . . . . . . . . . . . . . . . . . . 62
5.4. Retorno de valores de funciones . . . . . . . . . . . . . . . . . . . . . . 63
5.5. Variables globales y estticas . . . . . . . . . . . . . . . . . . . . . . . 64
5.6. Flujo de ejecucin (memoria de cdigo) . . . . . . . . . . . . . . . . . 64
5.7. Pila . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 66
5.8. Biblioteca de funciones matemticas . . . . . . . . . . . . . . . . . . . 67
6. Implementacin 69
6.1. Creacin de patrones . . . . . . . . . . . . . . . . . . . . . . . . . . . . 75
6.1.1. dene_insn . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 75
6.1.2. Ejemplo de dene_insn . . . . . . . . . . . . . . . . . . . . . . 76
6.1.3. Dene_expand . . . . . . . . . . . . . . . . . . . . . . . . . . . 79
6.2. Asignacin - los patrones mnimos. . . . . . . . . . . . . . . . . . . . . 80
6.3. Los patrones del back-end . . . . . . . . . . . . . . . . . . . . . . . . . 88
6.4. Especicacin . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 93
7. Anlisis del cdigo generado 105
7.1. Asignacin . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 105
Compilador de C para el microcontrolador Microchip PIC18F4550 3
7.2. Operaciones matemticas simples . . . . . . . . . . . . . . . . . . . . 108
7.2.1. Suma . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 108
7.2.2. Resta . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 110
7.2.3. Negacin . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 112
7.2.4. Valor absoluto . . . . . . . . . . . . . . . . . . . . . . . . . . . . 112
7.3. Operaciones lgicas . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 113
7.3.1. AND, OR y XOR . . . . . . . . . . . . . . . . . . . . . . . . . . 113
7.3.2. Negacin Lgica o Complemento a 1 . . . . . . . . . . . . . . 113
7.3.3. Desplazamientos . . . . . . . . . . . . . . . . . . . . . . . . . . 114
7.4. Conversin de tipos o casting . . . . . . . . . . . . . . . . . . . . . . . 115
7.4.1. Extensin de signo . . . . . . . . . . . . . . . . . . . . . . . . . 115
7.4.2. Extensin de ceros . . . . . . . . . . . . . . . . . . . . . . . . . 116
7.5. Operaciones matemticas complejas . . . . . . . . . . . . . . . . . . . 117
7.5.1. Multiplicacin . . . . . . . . . . . . . . . . . . . . . . . . . . . . 117
7.5.2. Divisin . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 118
7.6. Estructuras condicionales o de seleccin . . . . . . . . . . . . . . . . . 119
7.6.1. if . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 119
7.6.2. switch . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 124
7.7. Estructuras iterativas o bucles . . . . . . . . . . . . . . . . . . . . . . . 127
7.7.1. while . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 127
7.7.2. do-while . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 128
7.7.3. for . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 129
7.8. Funciones . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 131
7.8.1. Genricas . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 131
7.8.2. main . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 134
7.9. Archivos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 135
7.9.1. Genricos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 135
7.9.2. Principal . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 136
8. Optimizando cdigo con GCC 141
8.1. Tipos de optimizacin . . . . . . . . . . . . . . . . . . . . . . . . . . . 141
9. Conclusiones 147
Bibliografa 149
A. PIC18-GCC 150
A.1. Compilando GCC . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 150
A.2. Utilidades . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 151
Compilador de C para el microcontrolador Microchip PIC18F4550 4
A.3. Argumentos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 152
A.4. Parmetros de GCC especcos para el PIC18 . . . . . . . . . . . . . . 153
A.5. Compilando con pic18-gcc . . . . . . . . . . . . . . . . . . . . . . . . . 153
B. GNU PIC Utilities 155
B.1. Introduccin . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 155
B.2. Herramientas . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 156
B.2.1. gpasm . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 156
B.2.2. Cdigo fuente admitido por gpasm . . . . . . . . . . . . . . . 157
B.2.3. gplink . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 160
B.2.4. gplib . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 161
B.3. Compilando ensamblador . . . . . . . . . . . . . . . . . . . . . . . . . 162
C. Programas de ejemplo 165
D. Manual de usuario 170
D.1. GCC . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 170
D.1.1. Ensamblador en lnea . . . . . . . . . . . . . . . . . . . . . . . 171
D.2. GNU Pic Utilities . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 172
D.2.1. gpasm . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 173
D.2.2. gplink . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 173
D.2.3. gplib . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 173
D.2.4. Programador . . . . . . . . . . . . . . . . . . . . . . . . . . . . 174
D.2.5. Archivos auxiliares . . . . . . . . . . . . . . . . . . . . . . . . . 175
Captulo 1
Introduccin
La mayora de los programadores de microcontroladores preeren usar lenguaje
ensamblador en lugar de un lenguaje de alto nivel. Sus argumentos son el desper-
dicio de memoria que introduce un compilador y la mayor optimizacin que llegan
a conseguir escribiendo su propio cdigo ensamblador personalizado, as como la
elevada predecibilidad del tiempo de ejecucin de los programas escritos en ensam-
blador. Pese a que puede demostrarse que es cierto en la mayora de los casos, hay
otros muchos factores que inclinan la balanza hacia un lenguaje ms alejado de la
arquitectura de la mquina, como es el lenguaje C.
Para propsitos de experimentacin o aprendizaje, la eleccin de un lenguaje u
otro puede apoyarse nicamente en las preferencias personales del desarrollador.
Sin embargo, en entornos de desarrollo de productos comerciales hay que conside-
rar dos factores muy importantes: tiempo de desarrollo y coste del mismo. Reducir
el coste es un objetivo comn en todas las empresas y, en un principio, no tiene
relacin directa con el lenguaje de programacin elegido. En cambio, el tiempo in-
vertido en el desarrollo s est ligado al lenguaje por el que se opte, especialmente
cuando las opciones son lenguaje ensamblador o uno de alto nivel, en nuestro caso
C.
El desarrollo en lenguaje ensamblador siempre depende del dispositivo elegido
para realizar el proyecto. En el caso de dispositivos integrados como los micro-
controladores, cambiar de modelo puede implicar un comienzo desde cero, con la
enorme prdida de tiempo y dinero que esto supondra. En cambio, un cdigo en
lenguaje C es independiente de la arquitectura del procesador, por lo que slo ha-
bra que preocuparse del control de los dispositivos perifricos. Desarrollando en
un lenguaje de alto nivel, un cambio de modelo de microcontrolador a otro (de
la misma familia y compatible en dispositivos) resultara totalmente transparente,
siempre suponiendo que el nuevo dispositivo disponga de suciente memoria para
el programa. Incluso sera posible cambiar de familia de dispositivos, por ejemplo
5
Compilador de C para el microcontrolador Microchip PIC18F4550 6
de PIC18 a PIC16, sin ms cambios que adaptar el cdigo especco que gestiona
los dispositivos perifricos.
Relacionado con el punto anterior est el hecho de que el cdigo escrito en un
lenguaje de alto nivel como C es fcilmente reutilizable. Por consiguiente, si dis-
ponemos de un algoritmo ya implementado, probado, depurado y, por supuesto,
comentado, podremos utilizarlo en otros proyectos sin necesidad de invertir gran-
des cantidades de tiempo en reescribirlo. Si el mismo algoritmo estuviese escrito en
cdigo ensamblador para un dispositivo concreto, su reutilizacin sera mucho ms
cara, ya que portarlo a otra arquitectura supone, prcticamente, reescribirlo desde
cero.
Adems de las ventajas de portabilidad y reutilizacin de cdigo, los lenguajes
de alto nivel facilitan la depuracin de errores. Debido a la dicultad de analizar y
vericar un programa en ensamblador, la fase depuracin resulta larga y tediosa, lo
que lleva a un tiempo de desarrollo en ocasiones excesivo y a que los clientes con
menos paciencia opten por cancelar el proyecto. En cambio, los lenguajes de alto
nivel permiten tcnicas de desarrollo y depuracin ms ecientes, e incluso es posi-
ble emplear tcnicas formales de vericacin. Por ello el tiempo necesario para esta
fase del desarrollo es menor que para el mismo proyecto escrito en ensamblador.
Todos estos son motivos de peso que justican el esfuerzo invertido en este pro-
yecto, cuyo objetivo es disponer de un compilador de lenguaje C completo que per-
mita mejorar la calidad de los programas escritos para el dispositivo escogido, el
microcontrolador PIC18F4550 de Microchip, as como reducir el tiempo necesario
para desarrollar proyectos de diversa ndole que empleen este microcontrolador.
Los motivos de escoger el compilador de GNU, GCC, como punto de partida
han sido su buen diseo, que despus de 25 aos de historia sigue siendo una pie-
za de ingeniera del software digna de admiracin; su desarrollo activo por parte
de una gran comunidad de desarrolladores experimentados; su amplia difusin y
aceptacin por usuarios, profesionales y empresas de todo el mundo; su naturaleza
de cdigo abierto y, por supuesto, la licencia libre bajo la cual se distribuye, la General
Public License (Licencia Pblica General) de GNU, que permite en ltima instancia que
este proyecto nal de carrera sea posible.
El objetivo del proyecto ha sido desarrollar el backend de GCC para el microcon-
trolador PIC18F4550. Aunque, por tratarse de un proyecto de la Ingeniera Tcnica
acotamos la dicultad del proyecto en un principio, excluyendo funciones, punteros
y matrices de este proyecto, nalmente hemos implementado tambin funciones,
punteros y matrices, dando soporte completo al frontend de C para el PIC18F4550.
Para ello, y aunque en principio el plan era ampliar el backend para microcontro-
ladores PIC16 desarrollado en [8], hemos terminado reescribiendo desde cero un
Compilador de C para el microcontrolador Microchip PIC18F4550 7
backend propio; ya que, aunque las diferencias en el cdigo en ensamblador son pe-
queas, las diferencias en el manejo de la memoria lo han justicado, y adems nos
ha permitido utilizar las instrucciones especcas del 18F4550, que suponen una
mejora de rendimiento del cdigo resultante muy importante.
Hemos realizado el proyecto para la plataforma Linux, aunque por su naturaleza
y por el grado de integracin con la GCC toolchain alcanzado puede ser recompilado
para cualquier plataforma que queramos emplear y que permita ejecutar la GCC
toolchain.
Finalmente, hemos hecho un sobreesfuerzo para cumplir las lneas de desarrollo
de cdigo de GCC, y no afectar cdigo de GCC fuera del que se espera que sea
modicado en el desarrollo de un porting limpio, con objeto de su futura integracin
en la lnea principal de desarrollo de GCC.
1.1. Estructura de la memoria
La estructura de la memoria es la siguiente:
En el primer captulo presentamos el trabajo desarrollado y sus partes.
En el segundo captulo introducimos los microcontroladores y la arquitectura
PIC.
En el tercer captulo analizamos la arquitectura del compilador GCC.
En el cuarto captulo planteamos el diseo de nuestra solucin, as como las
opciones que hemos tomado y justicamos dichas opciones.
En el quinto captulo estudiamos con ms detalle aspectos destacables de las
decisiones que hemos tomado al implementar el backend, incluyendo anlisis
de las optimizaciones de cdigo soportadas.
En el sexto captulo comentamos las conclusiones a las que hemos llegado en
nuestro trabajo.
Despus planteamos cuatro apndices, que incluyen:
En el primer apndice, la mecnica (que no es trivial) para incorporar nuestro
cdigo en GCC y compilarlo.
En el segundo apndice describimos las utilidades de GNU para microcontro-
ladores PIC, imprescindibles para utilizar nuestro proyecto: GPUTILS.
Compilador de C para el microcontrolador Microchip PIC18F4550 8
En el tercer apndice incluimos algunos programas de ejemplo.
Finalmente, en el cuarto y ltimo apndice, incluimos el manual de usuario
del programa desarrollado.
Captulo 2
Los microcontroladores PIC
2.1. Qu es un microcontrolador?
Un microcontrolador es un dispositivo integrado que proporciona las funciona-
lidades de un pequeo ordenador. Los microcontroladores estn compuestos por
un procesador, memoria y varios perifricos. Las grandes ventajas de los microcon-
troladores son su reducido tamao, bajo coste y gran variedad.
Los dispositivos que proporcionan datos de entrada al microcontrolador o reci-
ben informacin de salida desde el mismo pueden ser muy diversos, desde lneas
digitales sencillas que slo trabajan con dos valores (cero o uno) hasta puertos com-
plejos, como los que se emplean en ordenadores, que permiten la comunicacin del
microcontrolador con otros dispositivos externos, tales como otros microcontrola-
dores o un ordenador personal.
Existen numerosos fabricantes que ofrecen una gran variedad de modelos dife-
rentes. Cada modelo tiene unas caractersticas: tamao, cantidad de memoria, po-
tencia de clculo, perifricos, consumo de energa, resistencia al calor y resistencia
a la humedad. El objetivo de esta diversidad es la reduccin de costes, ya que no es
razonable exigir un dispositivo potente y completo que se adece a cualquier pro-
yecto y que adems sea barato. Cuantas ms caractersticas tenga un dispositivo,
mayor ser su coste de produccin; y, por consiguiente, mayor precio de adquisi-
cin. Por ello, la clave reside en hacer diseos sencillos y con unas caractersticas
limitadas.
Dada la variedad de microcontroladores existente, no nos resultar difcil encon-
trar un modelo que se ajuste a los requisitos de nuestro proyecto y al mismo tiempo
resulte econmico.
9
Compilador de C para el microcontrolador Microchip PIC18F4550 10
2.2. Aplicaciones
La gran diversidad de modelos disponibles en el mercado nos permite afrontar
innidad de diseos diferentes, por simples o complejos que estos sean. Algunos
ejemplos de uso real de microcontroladores son telfonos mviles, controles de ac-
ceso a edicios, mandos a distancia, sistemas de freno ABS, micro-robtica, redes
de sensores o sistemas de control de riego.
Ala hora de realizar un proyecto hardware la dicultad no reside en el uso de un
microcontrolador concreto, sino en la eleccin del fabricante y el modelo adecuado
para nuestro proyecto. Aunque el uso de microcontroladores nos facilitar la tarea
en muchos aspectos, debemos tener siempre presente el objetivo de minimizar los
costes. En ocasiones encontraremos aplicaciones en las que no ser factible emplear
un nico microcontrolador o en las que un diseo distribudo con varios microcon-
troladores resultar ms eciente y econmico. Por este motivo, resulta conveniente
realizar un anlisis de costes antes de comenzar el desarrollo.
2.3. Historia de los microcontroladores PIC
En el mercado de microcontroladores, tres empresas son las ms conocidas: At-
mel, Motorola y Microchip. Existen muchas otras, como Intel, Texas Instruments o Re-
nesas Technology.
Microchip es el fabricante de los microcontroladores PIC. Todos ellos son hereda-
dos del PIC1650, que fue desarrollado originalmente por General Instruments.
El nombre PIC es la abreviatura de PICmicro, aunque por lo general se considera
acrnimo de Peripheral Interface Controller (Controlador de Interfaz Perifrico).
En un principio, el PIC se dise para combinarse con el procesador de 16 bits
CP16000 -de ah que el nombre original fuese Programmable Interface Controller (Con-
trolador de Interface Programable)-. El CP16000, a pesar de ser un buen microproce-
sador, careca de una buena E/S y por ello, en 1975, surgi el PIC de 8 bits, pensado
para mejorar el rendimiento del sistema conjunto mediante la descarga de operacio-
nes de E/S del CP16000. El PIC original empleaba un microcdigo simple almacena-
do en ROM para ejecutar su tarea y, a pesar de que el trmino no exista en aquella
poca, posea caractersticas tpicas de los diseos RISC.
Cuando la divisin de microelectrnica de General Instruments se separ del res-
to de la empresa, en 1985, el nuevo propietario cancel casi todos los desarrollos,
que para esa poca estaban obsoletos. Sin embargo, el PIC se mejor con EPROM,
convirtindose en un controlador programable de E/S.
A da de hoy existen multitud de modelos de PIC que incorporan varios puertos
Compilador de C para el microcontrolador Microchip PIC18F4550 11
de comunicacin con el exterior (puertos serie, controladores de motores o conver-
sores analgico-a-digital) y con memorias de programa de hasta 32.000 palabras.
2.4. Las gamas de PIC
Los modelos de PIC constituyen gamas distintas, en funcin del tamao de ins-
truccin que emplean. Actualmente, Microchip comercializa sus microcontroladores
clasicados en cuatro gamas:
La gama baja la componen la serie PIC10 y una parte de las series PIC12 y
PIC16. Utilizan palabras de instruccin de 12 bits, su tamao es reducido, as
como sus caractersticas, y su coste es muy bajo.
La gama media est compuesta por casi toda la serie PIC16 y una porcin de los
PIC12. Utilizan un ancho de palabra de instruccin de 14 bits. sta es la gama
ms popular por su buena relacin calidad/precio. Adems, programarlos en
lenguaje ensamblador resulta bastante sencillo, dentro de la complejidad del
lenguaje, y es por ello que son una buena opcin de cara al aprendizaje.
La gama alta o de alto rendimiento la forma la serie de microcontroladores
PIC18. Emplean palabras de instruccin de 16 bits y estn basados en los PIC
de gama media pero con mejoras sustanciales: ms puertos de E/S, ms con-
versores A/D o interfaces USB.
La gama de 24 bits la componen de las series dsPIC30 y PIC24. Utilizan 24 bits
como palabra de instruccin, usan palabras de memoria de datos de 16 bits (y
no 8 bits), y son los que ofrecen ms memoria y mayor rendimiento.
2.5. Los PIC de gama alta
La gama alta ofrece microcontroladores adecuados para proyectos relativamente
complejos, tales como adquisicin de datos, redes de sensores distribuidos e incluso
algunas aplicaciones de tiempo real. Adems, incorpora varias caractersticas que,
combinadas con un buen compilador, hacen de esta gama una opcin muy intere-
sante para ejecutar programas estructurados y modulares, escritos en lenguajes de
alto nivel y haciendo uso de tcnicas ms avanzadas de ingeniera del software, con
el ahorro de tiempo y dinero que esto supone.
En el captulo 3 estudiaremos la arquitectura de los PIC de gama alta.
Compilador de C para el microcontrolador Microchip PIC18F4550 12
2.6. El PIC18F4550
De entre todos los modelos que componen la gama alta de PIC, el elegido para
realizar el port de GCC es el 18F4550. Implementa ocho bancos de memoria de datos,
2Kbytes en total, y 32Kbytes de memoria de programa. Adems incorpora 256 bytes
de EEPROM para uso general. Cuenta con cuatro temporizadores, dos unidades de
comparacin, captura y modulacin de ancho de pulso, un conversor A/D de 10
bits con trece entradas. Adems, cuenta con puertos SPI, I
2
C, USART, SPP, USB.
Junto con estos dispositivos, tiene -multiplexados- un total de 5 puertos de E/S.
Todas estas caractersticas hacen del PIC18F4550 un gran microcontrolador con el
que se pueden llevar a cabo innidad de proyectos de diversa ndole.
Captulo 3
Arquitectura PIC
3.1. Diseo del PIC
Los microcontroladores PICemplean la arquitectura Harvard, en la que la memo-
ria de programa y la memoria de datos se encuentran separadas, con sus propios
buses de direcciones y datos.
Una ventaja de esta arquitectura frente a la Von Neumann es que permite el acce-
so simultneo a ambas memorias, con lo que es posible solapar las etapas de bs-
queda de la siguiente instruccin mientras la instruccin actual accede al espacio
de memoria de datos. El principal inconveniente de esta arquitctura surge con el
uso de memoria cach, dado que cada espacio de memoria necesitar su propia ca-
ch independiente, y el rendimiento mximo slo se alcanzar con programas que
hagan un nmero similar de accesos a memorias de programa y datos. En caso con-
trario, el espacio menos usado tendr parte de su cach sin utilizar, mientras que
la cach del otro se encontrar siempre llena, con las posibles penalizaciones oca-
sionadas por los fallos de cach que esto conlleva. En cualquier caso, los PIC no
incorporan memoria cach, as que este problema no les afecta y slo obtienen los
benecios de la arquitectura Harvard.
Otra mejora de la arquitectura Harvard es que el tamao de palabra de cada
banco de memoria puede ser diferente. En los microcontroladores PIC el banco de
memoria de datos emplea un tamao de palabra tpico, 8 bits, mientras que las pa-
labras de la memoria de programa son de distinto tamao en funcin de la gama: la
gama alta emplea palabras de 16 bits para este espacio de memoria. De este modo,
es posible incluir en una sola palabra todos los datos para la ejecucin de la instruc-
cin, mejorando la eciencia ya que se necesita un nico acceso a la memoria de
programa para cada instruccin.
Entre las decisiones de diseo que han permitido a los PIC lograr su alto rendi-
13
Compilador de C para el microcontrolador Microchip PIC18F4550 14
miento sin que esto penalice en su precio se encuentra el pipeline segmentado en dos
etapas: bsqueda de instruccin y ejecucin. Ambas etapas se ejecutan en un nico
ciclo de reloj. Gracias a la divisin de memoria, estas dos etapas se superponen en
el tiempo sin ninguna restriccin, con lo que se consigue completar la ejecucin de
una instruccin con cada ciclo de reloj en la mayora de los casos. Las nicas instruc-
ciones donde no es posible este solape son las que modican el contador de programa
(en ingls, program counter o PC), ya que la bsqueda de la prxima instruccin se
efecta en paralelo a la ejecucin de la actual y, hasta que no termine la ejecucin de
sta y se cargue el PC, no ser posible hacer la bsqueda de la siguiente instruccin.
As, todas las instrucciones de salto emplearn dos ciclos de reloj para ejecutarse.
El conjunto de instrucciones es reducido gracias a la ortogonalidad de las mis-
mas, que permiten mover contenidos desde y hacia cualquier registro. La curva de
aprendizaje del ensamblador del PIC es, por consiguiente, reducida. Esto, unido a
que todos los registros con funciones especiales se encuentran mapeados en me-
moria (includo el PC) nos proporciona la potencia y exibilidad necesarias para
manejar, de manera sencilla y elegante, todas las funciones del microcontrolador.
3.2. CPU
La Unidad Central de Proceso -en adelante, CPU- es el cerebro del microcon-
trolador. Su cometido es ejecutar la instruccin apuntada por el PC en la memoria
de programa mediante la generacin de la seales de control apropiadas sobre los
buses, registros internos y unidad aritmtico-lgica (ALU).
3.3. ALU
Los microcontroladores PIC incorporan una ALUde 8 bits. Adems tienen un re-
gistro de trabajo, tambin de 8 bits, llamado WREG (Working Register). La ALU efec-
ta operaciones aritmticas y lgicas de propsito general entre el registro WREG y
cualquier otro registro de la memoria de datos, o entre WREG y un valor inmediato,
tomado de la palabra de la instruccin en curso.
La salida de la ALU puede almacenarse en el registro WREG o en el registro
de la memoria de datos empleado como parmetro. En funcin de la instruccin
ejecutada, la ALU actualizar los bits apropiados del registro de estado (STATUS),
indicando acarreo, acarreo de dgito, desbordamiento, resultado negativo o cero.
Con las operaciones que soporta la ALU y la informacin que devuelve es posible
implementar cualquier operacin matemtica, como se ver ms adelante en este
Compilador de C para el microcontrolador Microchip PIC18F4550 15
documento.
La gama alta de PIC incorpora, adems de la ALU estndar, un multiplicador
hardware de valores de 8 bits que permite efectuar multiplicaciones en un slo ciclo
de reloj y facilita el desarrollo de rutinas de multiplicacin para datos ms grandes,
as como de otras operaciones matemticas avanzadas.
3.4. Organizacin de la memoria
Como ya hemos visto, la memoria est dividida en dos espacios, uno para datos
y otro para programa. La memoria de datos, a su vez, se encuentra organizada en re-
gistros que podemos clasicar en registros de propsito general (GPR) y registros de
funcin especial (SFR), que controlan las funciones del ncleo del microcontrolador.
Ms adelante estudiaremos los registros especiales para controlar los perifricos.
3.4.1. Memoria de programa
La memoria de programa emplea palabras de 16 bits con una alineacin a ni-
vel de byte. Est conectada a un bus tambin de 16 bits. Dado que una instruccin
ocupa una palabra de la memoria de programa, podemos almacenar tantas instruc-
ciones como palabras de memoria tenga nuestro microcontrolador. Para direccionar
este banco de memoria se usa el contador de programa (PC), que direcciona bytes,
no palabras. Este registro tiene un ancho de 21 bits, lo que permite direccionar hasta
2Mbytes de memoria. Ante la posibilidad de que el PC quede desalineado respecto
a las palabras, su bit menos signicativo es siempre 0 y las instrucciones de eje-
cucin secuencial incrementan el contador de programa en dos unidades. De este
modo, el PC siempre apuntar a una palabra de 16 bits alineada a nivel de palabra.
Pese a que siempre podemos direccionar el mximo de memoria, los microcon-
troladores incorporan una cantidad de memoria inferior a la mxima que soportan.
Todos los accesos de lectura a posiciones de memoria por encima del lmite de me-
moria incorporada devolvern el valor cero.
PC
El contador de programa apunta a la direccin de la memoria de programa que
ser accedida en el prximo acceso. Como todos los registros de funcin especial,
est mapeado en la memoria de datos y sta emplea palabras de 8 bits. Dado que
el PC tiene un tamao de 21 bits, est dividido en tres registros mapeados en la
memoria de datos: PCL, PCH y PCU. PCL corresponde al byte bajo del PC (PC[7:0]),
Compilador de C para el microcontrolador Microchip PIC18F4550 16
PCH al byte alto (PC[15:8]) y PCU al byte superior, que almacena los 5 bits ms
signicativos del PC (PC[20:16]).
El registro PCL permite lectura y escritura de manera directa. En cambio los
otros dos registros, PCH y PCU, no son directamente accesibles por el programa-
dor. Para modicar estos registros hacemos uso de otros dos registros especiales,
que s permiten lectura y escritura: PCLATH y PCLATU. Estos registros se compor-
tan como registros temporales que almacenan los valores que cargarn PCH y PCU
cuando se realice una escritura en PCL. Por tanto, una modicacin del PC requie-
re tres operaciones, no necesariamente unidas, que tendrn efecto tras la carga de
PCL. De manera anloga, una lectura de PCL produce que se carguen en PCLATH y
PCLATU los valores de PCH y PCU respectivamente.
Instrucciones que modican el PC
Adems del procedimiento anterior, el ensamblador de PIC proporciona varias
instrucciones que modican el PC: GOTO, CALL, RCALL y RETURN. Todas ellas
modican el PC directamente, sin tocar los registros PCLATH y PCLATU.
La instruccin GOTO efecta un salto incondicional en el ujo de ejecucin del
programa a cualquier direccin del rango de 2Mbytes que permite el PIC18. Es fcil
deducir que se trata de una instruccin codicada mediante dos palabras de me-
moria, ya que, de lo contrario, el rango de salto sera menor.
El comportamiento de la instruccin CALL es similar al de GOTO. La diferencia
entre ellas es que CALL almacena en la pila interna la direccin de la instruccin
que se encuentra a continuacin de ella (PC+4).
Podemos considerar la instruccin RCALL como una versin recortada de la an-
terior. Produce saltos en el programa de hasta 1Khacia delante o hacia atrs, respec-
to al valor, incrementado en dos, que almacena el PC en el momento de ejecutarse.
La cuarta instruccin en realidad es un conjunto de tres instrucciones (RETURN,
RETLW y RETFIE) que, grosso modo, cumplen el mismo cometido. Cuando el mi-
crocontrolador ejecuta una de estas instrucciones, el PC carga el valor que se en-
cuentra almacenado en la cima de la pila.
Pila
Los PIC18 implementan una pila de direcciones de 31 niveles en una memoria
aparte de los espacios de programa y datos. Es posible leer y modicar el puntero a
la cima de la pila (STKPTR), as como la direccin almacenada en la cima, median-
te los registros de funcin especial de cima de pila. Hay que tener en cuenta que
tiene un comportamiento circular, de modo que tras efectuar 31 acciones seguidas
Compilador de C para el microcontrolador Microchip PIC18F4550 17
de apilamiento el puntero a la cima habr dado la vuelta y quedar apuntando a la
primera posicin, lo que podra no ser el comportamiento deseado por el progra-
mado. Por ello es necesario tener mucha precaucin de no sobrepasar los 31 niveles
de anidamiento de subrutinas.
Vectores
Para terminar con la descripcin de la memoria de cdigo, veremos dos po-
siciones importantes para la programacin de los dispositivos. En el momento de
inicializarse el microcontrolador, la ejecucin del programa comienza en la posicin
de memoria de programa 0x000000h, llamada vector de reset. Es desde esta posicin
donde debemos insertar un salto al inicio real de nuestro cdigo (el inicio deseado),
a travs de un salto con GOTO u otro de los mtodos alternativos.
Existen otros dos vectores ubicados en 0x000008h y 0x000018h, correspondientes
a las interrupciones de alta y baja prioridad, respectivamente. Cada vez que se acti-
ve una seal de interrupcin, el microcontrolador deshabilitar las interrupciones,
guardar el valor del PC en pila, cargar el nuevo valor de PC a partir del almace-
nado en uno de estos dos vectores, en funcin de la seal de interrupcin que se
haya activado, y continuar ejecutando instrucciones. Esta rutina de tratamiento de
interrupcin (ISR) deber terminar con una instruccin RETFIE, con la que el PC
recupera su valor a partir de la pila, el registro GIE vuelve a estar habilitado y con
l, las interrupciones. Hay que tener en cuenta que disponemos de dos niveles de
interrupciones: alta y baja prioridad. La distincin de prioridades permite que los
acontecimientos de alta prioridad reciban atencin inmediata an cuando el micro
est tratando otra interrupcin -siempre que sta sea de prioridad baja- y que su
rutina de servicio no se vea interrumpida por otros eventos de menor prioridad.
3.4.2. Memoria de datos
La memoria de datos est compuesta por registros de propsito general (GPR) y
registros de funcin especial (SFR). Los primeros sirven para almacenar cualquier
valor sin un propsito establecido de antemano. Los segundos tienen tareas de-
nidas y jadas en el diseo del hardware: comunicar y controlar los diversos dis-
positivos integrados en el microcontrolador y controlar el funcionamiento de ste.
Debido a la diversidad de microcontroladores, el mapa de registros diere para ca-
da modelo. A pesar de que existe una tendencia a igualar la ubicacin en memoria
de los SFR para microcontroladores de una misma familia y gama, es convenien-
te consultar la hoja de caractersticas del modelo, incluso de nuevas revisiones del
mismo, para tener la certeza de que el dispositivo funcionar de la manera espe-
Compilador de C para el microcontrolador Microchip PIC18F4550 18
Banco Bits 3:0
accesible de BSR
0 0 0 0 0
1 0 0 0 1
2 0 0 1 0
3 0 0 1 1
4 0 1 0 0
5 0 1 0 1
6 0 1 1 0
7 0 1 1 1
Cuadro 3.1: Valores del BSR para acceso a los bancos del microcontrolador
PIC18F4550.
rada. Los registros de los perifricos los describiremos ms adelante, en la seccin
correspondiente a cada perifrico.
Los PIC18 pueden direccionar hasta 4Kbytes de memoria de datos que, como
ocurre con el espacio de memoria de programa, pueden estar implementados en su
totalidad o disponer de menos memoria. Esta memoria est dividida en varias par-
tes, llamadas bancos, de 256 bytes cada una. El PIC18F4550 implementa 8 bancos
completos, lo que resulta en un total de 2Kbytes de memoria de datos. Igual que
ocurre con la memoria de programa, todo acceso de lectura a una posicin de me-
moria fuera del rango implementado devuelve un valor 0. Los accesos de escritura
a direcciones de memoria no implementadas no tienen ningn efecto ms all de
actualizar el registro de estado STATUS como si la operacin hubiese tenido xito.
La mayora de instrucciones de los PIC18 que efectan accesos a memoria cons-
truyen la direccin absoluta a partir de una direccin base (el nmero de banco) y
un desplazamiento relativo. El nmero de banco al que acceder la prxima ins-
truccin lo indica el registro de seleccin de banco (BSR), el cual habr que cambiar
antes de hacer un acceso a una posicin de memoria ubicada en un banco diferente
al que se encuentre seleccionado en el momento actual. El desplazamiento dentro
del banco, en cambio, es una direccin de 8 bits includa en la palabra de instruc-
cin.
Banco de acceso
Aunque el uso del BSR junto a un desplazamiento de 8 bits permite direccionar
todo el rango de memoria de datos, tambin obliga a tener especial cuidado con
qu banco se encuentra seleccionado antes de cada acceso. Podra resultar desastro-
so que por error escribisemos en un SFR en lugar de un GPR. Adems, la tarea de
Compilador de C para el microcontrolador Microchip PIC18F4550 19
vericar y cambiar el BSR para cada acceso a memoria llega a resultar muy ine-
ciente.
Para agilizar los accesos a las posiciones de memoria que se usan con mayor fre-
cuencia, la memoria de datos est congurada con un banco de acceso, que permite
acceder a un bloque de memoria sin hacer uso del BSR. Este banco est compuesto
por los primeros 96 bytes del banco 0 y los ltimos 160 bytes del banco 15. La pri-
mera mitad recibe el nombre de RAM de acceso y est compuesta de registros GPR.
La mitad superior, en cambio, est compuesta por los SFR del microcontrolador.
Ambas reas se encuentran mapeadas de manera contigua en el banco de acceso y
permiten accesos de modo lineal empleando una direccin de 8 bits.
Las instrucciones que hacen uso del banco de acceso son aquellas que incluyen
un parmetro a. Cuando este parmetro vale 1, la instruccin accede a memoria de
la manera habitual, empleando el BSR y un desplazamiento de 8 bits. Cuando vale
0, el acceso a memoria lo efecta mediante el banco de acceso, con lo que el valor
que tenga el BSR no tiene efecto alguno.
El uso de este direccionamiento forzado permite a la instruccin operar sobre una
direccin de memoria en un nico ciclo de instruccin sin tocar primero el BSR. Para
direcciones a partir de la 0x0060h, esto signica un acceso ms eciente a los SFR.
La RAM de acceso por debajo de la posicin 0x0060h es un buen lugar para valores
de datos que necesitemos acceder rpidamente, como resultados de operaciones
recientes o variables globales del programa.
Registros de propsito general
Los registros de propsito general (GPR), como su nombre indica, no tienen un pro-
psito prejado de antemano: los podemos usar para lo que consideremos oportuno
(datos temporales, variables y valores constantes, entre otros usos). La modica-
cin de uno u otro no tiene ms relevancia que el cambio de su valor, por lo que
nos proporciona la libertad necesaria para ejecutar las acciones que deseemos en el
microcontrolador.
No todos los dispositivos implementan la totalidad de registros GPR. En estos
modelos es posible acceder a todo el mapa de registros pero los accesos de lectura
devolvern siempre un valor 0 y las escrituras no tendrn ms efecto que actualizar
los bits convenientes del registro STATUS en caso de que sean resultado de una
operacin de la ALU.
Compilador de C para el microcontrolador Microchip PIC18F4550 20
Registro Uso
INDFx
Registros para acceso indirecto
POSTINCx
POSTDECx
PREINCx
PLUSWx
FSRxH
Punteros a memoria para acceso indirecto
FSRxL
TMRxH
Cuenta de los temporizadores
TMRxL
TxCON Conguracin de los temporizadores
STATUS Registro de estado
PRODH Byte alto del resultado de multiplicador
PRODL Byte bajo del resultado de multiplicador
PCLATU Parte superior del PC
PCLATH Parte alta del PC
PCL Parte baja del PC
INTCON
Conguracin de las interrupciones INTCON2
INTCON3
PIR1
Estado de las interrupciones
PIR2
PIE1
Habilitacin de interrupciones
PIE2
IPR1
Asignacin de prioridad de las fuentes de interrupcin
IPR2
RCON Control de Reset y prioridad de interrupciones
Cuadro 3.2: Resumen de registros de funcin especial del ncleo del microcontrola-
dor PIC18F4550.
Registros de funcin especial
Los registros de funcin especial (SFR) son los registros que emplean la CPU y los
mdulos perifricos para controlar el funcionamiento del microcontrolador. Estn
mapeados en las ltimas posiciones del banco 15 de memoria de datos, desde la
posicin 0x0F60h hasta la 0x0FFFh.
Podemos clasicar los SFR en dos conjuntos: los registros correspondientes a
funciones del ncleo (ALU, Reset e interrupciones) y los que corresponden a fun-
ciones de los perifricos. La tabla 3.2 presenta un resumen de los registros de funcin
especial del ncleo. Los registros de funciones de perifricos los veremos ms ade-
lante.
Compilador de C para el microcontrolador Microchip PIC18F4550 21
Direccionamiento indirecto
Existe otro modo de acceso a los registros: el acceso indirecto. Este modo permite
acceder a posiciones de memoria sin indicar una direccin ja como parmetro de
la instruccin. Para ello hacemos uso de los pares de registros de seleccin de ar-
chivo (FSR), los cuales actan como punteros a las direcciones de memoria en las
que deseamos leer o escribir. En total, es posible direccionar 4K posiciones, luego
hacen falta 12 bits para este cometido. Los ocho bits menos signicativos estn al-
macenados en el registro FSRxL, mientras que los ms signicativos son los cuatro
bits ms bajos del registro FSRxH correspondiente. El PIC18F4550 ofrece 3 pares de
FSR, de modo que podemos emplear hasta tres apuntadores independientes para
acceso indirecto a memoria.
Una vez indicada la direccin en un par FSR, podemos acceder al contenido de
esta posicin de memoria a travs del registro INDF asociado al FSR. Los registros
INDFx podemos considerarlos virtuales, ya que no son registros fsicos implemen-
tados en memoria, a pesar de que estn mapeados en sta. Por ejemplo, un acceso
a INDF0 resulta ser un acceso al registro apuntado por FSR0H:FSR0L.
Cabe destacar que tanto los FSR como sus INDF estn mapeados en el banco de
acceso, por lo que no es necesario preocuparse por la seleccin de banco a la hora de
utilizarlos. Adems, la direccin de memoria empleada para direccionamiento indi-
recto se encuentra almacenada completamente en el par FSR, por lo que la gestin
de bancos tampoco es necesaria cuando hagamos accesos indirectos a memoria.
3.5. Juego de instrucciones
El conjunto de instrucciones de los PIC18 est dividido en cuatro categoras b-
sicas:
Orientadas a byte.
Orientadas a bit.
De literales.
De control.
Orientadas a tabla.
La mayora de instrucciones orientadas a byte tienen tres operandos:
1. Registro objetivo. f
Compilador de C para el microcontrolador Microchip PIC18F4550 22
2. Destino del resultado (WREG o un GPR). d
3. Banco de memoria accedido (datos o acceso). a
El registro objetivo f indica sobre qu registro operar la instruccin. El destino
d especica dnde almacenar el resultado de la operacin. Si d es cero, el resultado
se guarda en WREG. Si d es uno, se almacena en el mismo registro objetivo. El bit de
acceso a puesto a cero indica que el registro objetivo pertenece al banco de acceso. Si
a vale uno, entonces el registro objetivo corresponde al banco de memoria indicado
en BSR.
Todas las intrucciones orientadas a bit tiene tres operandos:
1. Registro objetivo. f
2. Bit del registro. b
3. Banco de memoria accedido (datos o acceso). a
El operando b indica el bit afectado por la instruccin. Los otros dos parmetros
tienen el mismo signicado que en las instrucciones orientadas a byte.
Las instrucciones de literales pueden tener uno de los siguientes operandos o
los dos:
Valor literal para cargar en un registro. k
Registro FSR donde cargar el valor literal. f
Las instrucciones de control pueden tener uno o varios de los siguientes operan-
dos:
Direccin de memoria de programa. n
Modo de las instrucciones CALL o RETURN. s
Las instrucciones orientadas a tabla tan slo tienen un operando de modo, que
indica si se trata de un acceso con pre-incremento, post-incremento, post-decremento
o un acceso simple que no modica el apuntador de tabla.
Las instrucciones ensamblador las veremos con una simple tabla-resumen. La
mejor fuente para ampliar informacin, una vez ms, es el datasheet del PIC18F4550,
que proporciona ejemplos para cada instruccin de las distintas posibilidades de
ejecucin. La tabla 3.5 muestra todas las instrucciones separadas por formato de
instruccin mostrando una pequea descripcin. La tabla 3.5 complementa a s-
ta, mostrando los ciclos que necesita cada instruccin, los bits del registro STATUS
afectados y la palabra genrica de cdigo mquina correspondiente a cada nemo-
tcnico.
Compilador de C para el microcontrolador Microchip PIC18F4550 23
Nemotcnico Descripcin
Orientadas a byte
ADDWF f,d,a Suma de WREG y f
ADDWFC f,d,a Suma con acarreo de WREG y f
ANDWF f,d,a And lgico de WREG y f
CLRF f,a Puesta a 0 de f
COMF f,d,a Complemento a 1 de f
CPFSEQ f,a Compara f y WREG, salta si igual
CPFSGT f,a Compara f y WREG, salta si mayor
CPFSLT f,a Compara f y WREG, salta si menor
DECF f,d,a Decrementa f
DECFSZ f,d,a Decrementa f, salta si cero
DCFSNZ f,d,a Decrementa f, salta si no cero
INCF f,d,a Incrementa f
INCFSZ f,d,a Incrementa f, salta si cero
INFSNZ f,d,a Incrementa f, salta si no cero
IORWF f,d,a Or lgico de WREG y f
MOVF f,d,a Carga f en WREG
MOVFF fs,fd Mueve de fs a fd
MOVWF f,a Almacena WREG en f
MULWF f,a Multiplica WREG con f
NEGF f,a Complemento a 2 de f
RLCF f,d,a Rotacin a la izquierda con acarreo
RLNCF f,d,a Rotacin a la izquierda sin acarreo
RRCF f,d,a Rotacin a la derecha con acarreo
RRNCF f,d,a Rotacin a la derecha sin acarreo
SETF f,a Puesta a 0xFFh de f
SUBFWB f,d,a Resta con acarreo WREG menos f
SUBWF f,d,a Resta sin acarreo f menos WREG
SUBWFB f,d,a Resta con acarreo f menos WREG
SWAPF f,d,a Intercambia los nibbles de f
TSTFSZ f,a Comprueba f, salta si cero
XORWF f,d,a Xor lgico de WREG y f
Cuadro 3.3: Resumen del conjunto de instrucciones del
microcontrolador PIC18F4550.
Compilador de C para el microcontrolador Microchip PIC18F4550 24
Orientadas a bit
BCF f,b,a Limpia un bit de f
BSF f,b,a Activa un bit de f
BTFSC f,b,a Comprueba un bit de f, salta si es 0
BTFSS f,b,a Comprueba un bit de f, salta si es 1
BTG f,d,a Conmuta un bit de f
De literales
ADDLW k Suma k a WREG
ANDLW k And lgico de k y WREG
IORLW k Or lgico de k y WREG
LFSR f,k Carga k en el FSR indicado por F
MOVLB k Carga k en el BSR
MOVLW k Carga k en el WREG
MULLW k Multiplica k con WREG
SUBLW k Resta k menos WREG
XORLW k Xor lgico de WREG y k
Cuadro 3.3: Resumen del conjunto de instrucciones del
microcontrolador PIC18F4550.
Compilador de C para el microcontrolador Microchip PIC18F4550 25
De control
BRA n Salto relativo incondicional
BC n Salto relativo si acarreo
BZ n Salto relativo si cero
BOV n Salto relativo si desbordamiento
BN n Salto relativo si negativo
BNC n Salto relativo si no acarreo
BNN n Salto relativo si no negativo
BNOV n Salto relativo si no desbordamiento
BNZ n Salto relativo si no cero
CALL n,s Llamada a subrutina
CLRWDT Puesta a 0 del temporizador del WDT
DAW Ajuste decimal de WREG
GOTO n Salto absoluto
NOP No operation
POP Extrae de la pila y carga en PC
PUSH Introduce en la pila el valor de PC
RCALL n Llamada a subrutina, relativa a PC
RESET Reset por software del dispositivo
RETFIE s Fin de ISR
RETLW k Fin de subrutina y carga k en WREG
RETURN s Fin de subrutina
SLEEP Pasa el dispositivo al modo de reposo
Cuadro 3.3: Resumen del conjunto de instrucciones del
microcontrolador PIC18F4550.
Compilador de C para el microcontrolador Microchip PIC18F4550 26
Orientadas a tabla
TBLRD* Lectura de tabla
TBLRD*+ Lectura con post-incremento
TBLRD*- Lectura con post-decremento
TBLRD+* Lectura pre-incremento
TBLWT* Escritura de tabla
TBLWT*+ Escritura con post-incremento
TBLWT*- Escritura con post-decremento
TBLWT+* Escritura con pre-incremento
Cuadro 3.3: Resumen del conjunto de instrucciones del
microcontrolador PIC18F4550.
Compilador de C para el microcontrolador Microchip PIC18F4550 27
Nemotcnico Ciclos Bis de STATUS afectados Palabra de instruccin
Orientadas a byte
ADDWF f,d,a 1 C,DC,Z,OV,N 0010 01da ffff ffff
ADDWFC f,d,a 1 C,DC,Z,OV,N 0010 00da ffff ffff
ANDWF f,d,a 1 Z,N 0001 01da ffff ffff
CLRF f,a 1 Z 0110 101a ffff ffff
COMF f,d,a 1 Z,N 0001 11da ffff ffff
CPFSEQ f,a 1-3 0110 001a ffff ffff
CPFSGT f,a 1-3 0110 010a ffff ffff
CPFSLT f,a 1-3 0110 000a ffff ffff
DECF f,d,a 1 C,DC,Z,OV,N 0000 01da ffff ffff
DECFSZ f,d,a 1-3 0010 11da ffff ffff
DCFSNZ f,d,a 1-3 0100 11da ffff ffff
INCF f,d,a 1 C,DC,Z,OV,N 0010 10da ffff ffff
INCFSZ f,d,a 1-3 0011 11da ffff ffff
INFSNZ f,d,a 1-3 0100 10da ffff ffff
IORWF f,d,a 1 Z,N 0001 00da ffff ffff
MOVF f,d,a 1 Z,N 0101 00da ffff ffff
MOVFF fs,fd 2
1100 ffff ffff ffff
1111 ffff ffff ffff
MOVWF f,a 1 0110 111a ffff ffff
MULWF f,a 1 0000 001a ffff ffff
NEGF f,a 1 C,DC,Z,OV,N 0110 110a ffff ffff
RLCF f,d,a 1 C,Z,N 0011 01da ffff ffff
RLNCF f,d,a 1 Z,N 0100 01da ffff ffff
RRCF f,d,a 1 C,Z,N 0011 00da ffff ffff
RRNCF f,d,a 1 Z,N 0100 00da ffff ffff
SETF f,a 1 0110 100a ffff ffff
SUBFWB f,d,a 1 C,DC,Z,OV,N 0101 01da ffff ffff
SUBWF f,d,a 1 C,DC,Z,OV,N 0101 11da ffff ffff
SUBWFB f,d,a 1 C,DC,Z,OV,N 0101 10da ffff ffff
SWAPF f,d,a 1 0011 10da ffff ffff
TSTFSZ f,a 1-3 0110 011a ffff ffff
XORWF f,d,a 1 Z,N 0001 10da ffff ffff
Cuadro 3.4: Instrucciones del microcontrolador
PIC18F4550: ciclos, bits de STATUS afectados y palabra
de instruccin.
Compilador de C para el microcontrolador Microchip PIC18F4550 28
Orientadas a bit
BCF f,b,a 1 1001 bbba ffff ffff
BSF f,b,a 1 1000 bbba ffff ffff
BTFSC f,b,a 1-3 1011 bbba ffff ffff
BTFSS f,b,a 1-3 1010 bbba ffff ffff
BTG f,d,a 1 0111 bbba ffff ffff
De literales
ADDLW k 1 C,DC,Z,OV,N 0000 1111 kkkk kkkk
ANDLW k 1 Z,N 0000 1011 kkkk kkkk
IORLW k 1 Z,N 0000 1001 kkkk kkkk
LFSR f,k 2
1110 1110 00ff kkkk
1111 0000 kkkk kkkk
MOVLB k 1 0000 0001 0000 kkkk
MOVLW k 1 0000 1110 kkkk kkkk
MULLW k 1 0000 1101 kkkk kkkk
SUBLW k 1 C,DC,Z,OV,N 0000 1000 kkkk kkkk
XORLW k 1 Z,N 0000 1010 kkkk kkkk
Cuadro 3.4: Instrucciones del microcontrolador
PIC18F4550: ciclos, bits de STATUS afectados y palabra
de instruccin.
Compilador de C para el microcontrolador Microchip PIC18F4550 29
De control
BRA n 2 1101 0nnn nnnn nnnn
BC n 1-2 1110 0010 nnnn nnnn
BZ n 1-2 1110 0000 nnnn nnnn
BOV n 1-2 1110 0100 nnnn nnnn
BN n 1-2 1110 0110 nnnn nnnn
BNC n 1-2 1110 0011 nnnn nnnn
BNN n 1-2 1110 0111 nnnn nnnn
BNOV n 1-2 1110 0101 nnnn nnnn
BNZ n 1-2 1110 0001 nnnn nnnn
CALL n,s 2
1110 110s kkkk kkkk
1111 kkkk kkkk kkkk
CLRWDT 1 TO,PD 0000 0000 0000 0100
DAW 1 C 0000 0000 0000 0111
GOTO n 2
1110 1111 kkkk kkkk
1111 kkkk kkkk kkkk
NOP 1 0000 0000 0000 0000
NOP 1 1111 xxxx xxxx xxxx
POP 1 0000 0000 0000 0110
PUSH 1 0000 0000 0000 0101
RCALL n 2 1101 1nnn nnnn nnnn
RESET 1 Todos 0000 0000 1111 1111
RETFIE s 2 GIE/GIEH, PEIE/GIEL 0000 0000 0001 000s
RETLW k 2 0000 1100 kkkk kkkk
RETURN s 2 0000 0000 0001 001s
SLEEP 1 TO,PD 0000 0000 0000 0011
Cuadro 3.4: Instrucciones del microcontrolador
PIC18F4550: ciclos, bits de STATUS afectados y palabra
de instruccin.
Compilador de C para el microcontrolador Microchip PIC18F4550 30
Orientadas a tabla
TBLRD* 2 0000 0000 0000 1000
TBLRD*+ 2 0000 0000 0000 1001
TBLRD*- 2 0000 0000 0000 1010
TBLRD+* 2 0000 0000 0000 1011
TBLWT* 2 0000 0000 0000 1100
TBLWT*+ 2 0000 0000 0000 1101
TBLWT*- 2 0000 0000 0000 1110
TBLWT+* 2 0000 0000 0000 1111
Cuadro 3.4: Instrucciones del microcontrolador
PIC18F4550: ciclos, bits de STATUS afectados y palabra
de instruccin.
3.6. Perifricos
A continuacin estudiaremos de forma resumida los distintos perifricos que
podemos encontrar en un PIC. Debido a la gran variedad de dispositivos integrados
que llegan a formar parte de un microcontrolador PIC y las diferencias existentes
en un dispositivo entre los distintos modelos, nos centramos en los dispositivos
contenidos en el microcontrolador 18F4550.
3.6.1. Puertos de Entrada/Salida
El puerto de E/S es el dispositivo ms sencillo de los existentes en un microcon-
trolador y tambin es el ms utilizado. Los PIC ofrecen puertos de E/S de hasta 8
bits de tamao. El 18F4550 dispone de 5 puertos de E/S, etiquetados con las letras
de la A hasta la E, de los cuales B y D son puertos de 8 bits, A y C son de 7 bits,
mientras que el puerto E tiene un ancho de 4 bits.
Cada puerto dispone de tres registros que permiten su manejo. El registro TRISx
(donde x es la letra que identica a cada puerto) indica el sentido (entrada o salida)
de cada pin, de modo que un bit puesto a 1 indica que el pin correspondiente es
de entrada, mientras que un 0 indica que dicho pin es de salida. Por tanto, para
emplear como pines de entrada los dos menos signicativos del puerto B y como
salida el resto de pines del mismo puerto basta con cargar en TRISB el valor binario
Compilador de C para el microcontrolador Microchip PIC18F4550 31
00000011. Una vez congurada la direccin, la lectura y escritura de un puerto de
E/S resulta tan sencilla como leer o escribir en el registro PORTx correspondiente.
El tercer registro asociado a un puerto, LATx, hace las funciones de latch de sa-
lida. Cuando efectuamos una operacin de escritura sobre PORTx, en realidad el
registro escrito es LATx. Sin embargo, una operacin de lectura sobre PORTx no
efecta una lectura del registro LATx, sino que produce la carga del dato de en-
trada en otro latch (destinado nicamente a datos entrantes). Acto seguido, el mi-
crocontrolador almacena el valor de este latch de entrada en el registro destino de
la instruccin. Gracias a esta combinacin de latches para entrada y salida es posi-
ble obtener el valor que sale del puerto sin necesidad de circuitera extra ni de un
puerto de entrada adicional. Un ejemplo que aprovecha esta conguracin es el par
de instrucciones BSF/BCF. Estas instrucciones cambian el valor de un bit almacena-
do en un registro cualquiera del microcontrolador. En el caso de un puerto de E/S,
primero leen el valor del registro (LATx en este caso), activan o desactivan el bit
indicado y, por ltimo, almacenan el nuevo valor en el latch de salida del puerto en
cuestin (LATx). Un detalle muy a tener en cuenta es que si un puerto de entrada,
que haya ledo un valor en algn momento, cambia de modo y pasa a funcionar en
modo de salida, el valor que tendr ser el almacenado en LATx (latch de salida de
PORTx), no el que haba en latch de entrada de PORTx.
Otro punto a tener en cuenta es que debido al coste que suponen las patillas
de comunicacin en los PIC y, en general, en cualquier dispositivo integrado, los
puertos de E/S estn multiplexados con otros dispositivos. Cuando uno de estos
dispositivos est activado, los pines compartidos con el puerto de E/S no podrn
usarse con tal propsito, con lo que el puerto tendr un tamao efectivo menor.
Un ejemplo de esto es el puerto A, cuyo pin 0 est multiplexado con el canal 0
del conversor analgico-digital. Cuando est activo el mdulo de conversin A/D,
habr que tener cuidado de no modicar los registros TRISA, TRISB y TRISE, ya
que afectan a los pines que emplea el conversor y podra resultar en errores de
conversin.
Por ltimo, hay que recordar que la escritura en los registros ocurre al nal del
ciclo de instruccin, mientras que la lectura se produce al principio. Debido a esto,
puede darse el caso de que el valor a la salida de un puerto no est estabilizado an-
tes del prximo acceso. Por este motivo, Microchip recomienda intercalar entre dos
operaciones consecutivas sobre un puerto concreto una instruccin que no haga uso
del mismo, para tener la seguridad de que los valores del puerto se han estabilizado
antes de un acceso de lectura.
Compilador de C para el microcontrolador Microchip PIC18F4550 32
3.6.2. Universal Serial Bus
En la actualidad, uno de los interfaces de comunicacin con perifricos ms uti-
lizados es el USB. El PIC18F4550 incorpora un motor de interface serie (SIE) que
soporta el estndar USB 2.0 en modos de baja y alta velocidad. El SIE puede cana-
lizarse directamente a dispositivos USB haciendo uso de su transceptor interno o
conectarse a un transceptor externo, proporcionando una notable exibilidad a la
hora de disear del hardware. El subsistema USB del 18F4550 tambin incorpora un
regulador de tensin de 3.3V con las resistencias de polarizacin exigidas por la es-
pecicacin USB 2.0. Adems, ofrece la posibilidad de usar polarizacin, e incluso
alimentacin externa, con el transceptor interno.
El control del subsistema USB recae sobre un total de 22 registros:
UCON; registro de control.
UCFG; registro de conguracin.
USTAT; registro de estado de transferencia.
UADDR; registro de direccin del dispositivo.
UFRMH y UFRML; registros de nmero de marco.
UEP0 a UEP15; registros de control de los terminadores.
El medio de intercambio de datos entre el controlador y el SIE es una memoria
de 1Kb de tamao, denominada USB RAM. Se trata de una memoria de 1Kb de
puerto doble, es decir, una memoria que permite accesos simultneos desde dos
dispositivos independientes, cada uno con sus propios buses de direcciones, datos
y seales de control. El puerto correspondiente al controlador est mapeado en los
bancos 4 a 7 de la memoria de datos.
3.6.3. SPP
Este modelo de PIC incorpora un puerto paralelo orientado a ujos de datos, en
ingls Streaming Parallel Port (SPP), pensado para utilizarse como interface de alta
velocidad para intercambiar grandes cantidades de datos con un sistema externo.
El SPP opera como puerto paralelo maestro completo, con seales de control y de
reloj para controlar el dispositivo esclavo conectado a l. Es posible congurarlo de
modo que el control del SPP recaiga sobre la CPU del PIC o sobre el controlador
USB descrito en la seccin anterior.
Compilador de C para el microcontrolador Microchip PIC18F4550 33
Los registros empleados para congurar el SPP son SPPCON, el cual controla el
modo de operacin y determina si el control recae sobre la CPU o sobre el SIE, y
el SPPCFG, que se encarga de la conguracin de temporizacin y de habilitar las
seales de control hacia el exterior. A estos se le suman los registros TRISD, TRISE
y TRISB, que indican en qu modo operarn los pines correspondientes a seales
de datos del SPP.
Cuando el SPP est congurado para que sea la CPU quien lo maneje, el regis-
tro SPPEPS es el que proporciona informacin de estado y control sobre el puerto,
mientras que SPPDATAes el registro objetivo de la lectura/escritura de datos trans-
mitidos a travs del puerto paralelo.
3.6.4. MSSP
El puerto serie sncrono maestro (MSSP) es un interface serie que resulta til para
comunicar el PIC con perifricos externos o con otros microcontroladores. Tiene dos
modos de funcionamiento diferentes: SPI (interface de perifrico serie) o como I
2
C. Los
registros empleados para controlar el MSSP son SSPSTAT, SSPCON y SSPCON2, el
primero de los tres proporciona informacin sobre el estado del mdulo y los dos
restantes cumplen las funciones de control. Los registros encargados de albergar los
datos a enviar o recibir son SSPSR y SSPBUF, mientras que el registro SSPADD es
el responsable de operaciones de direccionamiento cuando el mdulo est congu-
rado en modo I
2
C. Para conocer los detalles de su uso es conveniente acudir a la
seccin correspondiente del datasheet del microcontrolador. Adems, la web de Mi-
crochip contiene diversos ejemplos de aplicacin con cada uno de los modos de este
mdulo.
3.6.5. EUSART
Adems del MSSP, este PIC ofrece un segundo mdulo para comunicaciones se-
rie: el transmisor/receptor sncrono/asncrono universal mejorado (EUSART). Puede fun-
cionar en modos half-duplex y full-duplex, y adems incorpora caractersticas extra
sobre los USART convencionales, como deteccin y ajuste automticos de la tasa de
transferencia.
El control del mdulo EUSART recae sobre tres registros: TXSTA (control y es-
tado de la transmisin), RCSTA (control y estado de la recepcin) y BAUDCON
(control de la tasa de transferencia). El par de registros SPBRGH y SPBRG contro-
lan el perodo empleado por temporizador del generador de smbolos del EUSART.
Una vez que el mdulo est congurado para un modo de funcionamiento, el envo
Compilador de C para el microcontrolador Microchip PIC18F4550 34
comienza con la escritura del dato a enviar en el registro TXREG, mientras que una
recepcin con xito termina con el dato recibido en RCREG.
El proceso para transmitir un dato consta de los siguientes pasos:
1. Inicializar SPBRGH:SPBRG con el valor correspondiente a los baudios desea-
dos para la frecuencia actual del oscilador.
2. Congurar el mdulo EUSART como sncrono o asncrono segn la comuni-
cacin.
3. Habilitar la transmisin activando el bit TXEN de TXSTA.
4. Cargar TXREG con el valor que se desea transmitir. Esto hace que comience la
transmisin.
5. Comprobar el bit TRMT de TXSTA. Cuando est puesto a uno, la transmisin
habr nalizado.
De manera anloga, los pasos a seguir para recepcin son:
1. Inicializar SPBRGH:SPBRG con el valor correspondiente a los baudios desea-
dos para la frecuencia actual del oscilador.
2. Congurar el mdulo EUSART como sncrono o asncrono segn la comuni-
cacin.
3. Habilitar la recepcin activando el bit SREN de RCSTA.
4. Comprobar el bit RCIF de PIR1. Cuando est puesto a uno, la recepcin habr
nalizado y el registro RCREG contendr el dato recibido.
El valor que deber contener el par de registros SPBRGH:SPBRG establece el pe-
rodo del generador de smbolos, produciendo una velocidad en baudios diferente
segn la frecuencia de oscilador empleada. Cada extremo de la lnea de comuni-
caciones serie debe emplear la misma conguracin, de forma que transmisor y
receptor funcionen a la misma velocidad y en el mismo modo para que sea posi-
ble establecer con xito la comunicacin entre ambos. Para determinar el valor de
SPBRGH:SPBRG hace falta conocer la velocidad en baudios a la que debe funcionar
el EUSART. Una vez conocido este detalle, el valor de los registros SPBFGH:SPBRG
se calcula a partir de una de las frmulas siguientes, segn el modo de funciona-
miento en que vaya a trabajar el mdulo EUSART:
Como se deduce de la tabla anterior, el valor de SPBRGH:SPBRG depende tam-
bin del modo empleado para la comunicacin. Cabe destacar que la conguracin
de la comunicacin serie debe ser la misma en ambos extremos de la lnea.
Compilador de C para el microcontrolador Microchip PIC18F4550 35
Bits de conguracin
Modo SPBRGH:SPBRG Baudios
Sync BRG16 BRGH
0 0 0 8-bit asncrono (Fosc/(64*Baud))-1 Fosc/(64*(n+1))
0 0 1 8-bit asncrono
(Fosc/(16*Baud))-1 Fosc/(16*(n+1))
0 1 0 16-bit asncrono
0 1 1 16-bit asncrono
(Fosc/(4*Baud))-1 Fosc/(4*(n+1)) 1 0 x 8-bit sncrono
1 1 x 16-bit sncrono
Cuadro 3.5: Tabla de conguracin del EUSART del microcontrolador PIC18F4550.
Debido al redondeo al entero ms prximo, aparece un porcentaje de error res-
pecto a la tasa de transferencia terica deseada. Una prctica muy recomendable
es realizar la operacin contraria, tomando el valor de SPBRGH:SPBRG como dato
de partida y la tasa de baudios como incgnita, para calcular el error introducido
por el redondeo. Un error demasiado grande producir una desincronizacin entre
emisor y receptor, e imposibilitar la comunicacin entre ellos.
El registro TXSTA es el encargado del control del modo de transmisin. El bit
CSRC indica qu fuente de reloj emplear el EUSART para comunicacin sncrona.
TX9 permite seleccionar entre palabras de 8 o 9 bits. TXEN es el bit que habilita
la transmisin. SYNC, como indica su nombre, es el bit de seleccin entre modo
sncrono o asncrono; este bit tambin afecta al modo de recepcin. SENDB permi-
te forzar el envo, en comunicaciones asncronas, de un caracter de sincronizacin
(Sync Break). El control de la tasa de baudios (alta o baja velocidad) recae en el bit
BRGH. TRMT es un bit de estado, no de control; indica cundo est vaco el buf-
fer de salida. Por ltimo, el bit TX9D del registro TXSTA alberga el noveno bit de la
palabra a enviar cuando el EUSART est congurado para enviar palabras de 9 bits.
Por otro lado, el registro RCSTA controla la recepcin y da informacin sobre la
misma. RX9 indica el tamao de palabra, 8 o 9 bits. El bit SRENhabilita la recepcin
simple en modo sncrono, mientras que CREN habilita la recepcin mltiple, tam-
bin llamada continua. El bit de FERR indica si hubo un error de marco (frame) en la
recepcin, mientras que OERR avisa de que ha habido un error de desbordamiento.
RX9D es el bit anlogo a TX9D: almacena el noveno bit de la palabra recibida, si el
EUSART est trabajando con palabras de 9 bits.
3.6.6. Memoria EEPROM
El 18F4550 dispone de una pequea memoria EEPROM a la que es posible acce-
der, tanto para lectura como escritura, desde el programa en ejecucin. Su tamao
Compilador de C para el microcontrolador Microchip PIC18F4550 36
es reducido, slo 256 bytes. Esta memoria no est mapeada directamente sobre la
memoria de datos; la nica manera de acceder a ella es mediante direccionamiento
indirecto, a travs de varios SFR. EEADR es el registro de direccionamiento para
accesos a la EEPROM. Complementando a ste se encuentra el registro EEDATA;
en l aparecer el dato ledo de la EEPROM tras una operacin de lectura y es el
registro que aloja el dato a cargar en EEPROM con la prxima operacin de escri-
tura. Aparte de estos dos registros, de direccionamiento y datos, respectivamente,
hay dos registros ms, que cumplen las funciones de control de acceso: EECON1 y
EECON2.
EECON1 es un registro de control convencional, con varios bits que cumplen
distintos cometidos; es responsable del control de acceso a las memorias EEPROM,
ash de programa y a los bits de conguracin del PIC. El bit EEPGD selecciona a
memoria accedern las operaciones de lectura/escritura: ash de programa (uno) o
EEPROM de datos (cero). EECFGS determina si los accesos sern a los registros de
conguracin o a alguna de las dos memorias antes mencionadas. WRERR indica
si la ltima operacin de escritura termin con error. WREN es el bit que habilita
la operacin de escritura. La operacin de lectura comienza con la carga de un uno
en el bit RD del registro EECON1; una vez completada la operacin, el hardware del
microcontrolador carga de manera automtica un cero en este bit. La operacin de
escritura tiene un comportamiento anlogo con el bit WR.
El registro EECON2 no es un registro fsico. Su funcin se reduce a hacer de
seguro antes de una operacin de escritura o borrado. La nica manera de efectuar
una escritura es escribir el valor 0x55h en EECON2, acto seguido escribir 0xAAh en
este mismo registro, y, una vez hecho esto, poner a cero el bit WR de EECON1. En
caso de no seguir exactamente esta secuencia, el PIC no ejecutar la operacin de
escritura. Por supuesto, el bit WREN debe estar puesto a uno antes de efectuar estos
pasos. Como precaucin adicional, es muy recomendable tener deshabilitadas las
interrupciones durante la ejecucin de este fragmento de cdigo.
A continuacin, un ejemplo de cmo efectuar una lectura de la EEPROM:
; Cargar EEADR con la direccin deseada.
MOVLW direccin
MOVWF EEADR
; Seleccionar memoria EEPROM de datos.
BCF EECON1, EEPGD
; Activar acceso a memoria especial.
Compilador de C para el microcontrolador Microchip PIC18F4550 37
BCF EECON1, CFGS
; Leer de la EEPROM.
BSF EECON1, RD
; Cargar en W el dato ledo.
MOVF EEDATA, W
El cdigo anterior resulta sumamente sencillo. Comienza con la carga en el regis-
tro EEADR de la direccin de memoria EEPROM a la que acceder en esta operacin
de lectura. Acto seguido, pone a cero el bit EECFGS del registro de control EECON1,
con lo que indica al PIC que el prximo acceso a memoria especial ser a EEPROM
o ash. Lo siguiente es seleccionar la memoria EERPOM, que se consigue poniendo
a cero el bit EEPGD. Por ltimo, inicia la operacin de lectura mediante la puesta a
uno del bit RD. Este fragmento de cdigo concluye con la carga en WREG del dato
ledo de la EEPROM, el cual aparece alojado en EEDATA tras una lectura exitosa.
Un ejemplo prctico de cdigo que efecta una operacin de escritura:
; Mientras no haya terminado la posible
; escritura en proceso, hacer espera activa.
noWR BTFSC EECON1, WR
GOTO noWR
; Cargar EEADR con la direccin deseada.
MOVLW direccin
MOVWF EEADR
; Cargar EEDATA con el dato a almacenar.
MOVLW valor
MOVWF EEDATA
; Seleccionar EEPROM de datos.
BCF EECON1, EEPGD
; Activar acceso a memoria especial.
BCF EECON1, CFGS
; Habilitar las operaciones de escritura.
Compilador de C para el microcontrolador Microchip PIC18F4550 38
BSF EECON1, WREN
; Deshabilitar las interrupciones.
BCF INTCON, GIE
; Paso 1: EECON2 <- 0x55h
MOVLW 0x55
MOVWF EECON2
; Paso 2: EECON2 <- 0xAAh
MOVLW 0xAA
MOVWF EECON2
; Escribir en la EEPROM.
BSF EECON1, WR
; Volver a habilitar las interrupciones.
BSF INTCON, GIE
; Volver a deshabilitar la escritura.
BCF EECON1, WREN
Un primer vistazo de este otro fragmento de cdigo permite ver que las opera-
ciones de escritura resultan algo ms complicadas de efectuar que las de lectura,
sobre todo por las diversas precauciones que hay que tomar, tanto para poder ini-
ciar la escritura en s, como para asegurar que sta se efecta con xito y sin afectar
a una posible escritura en curso.
En primer lugar, hay que comprobar que no haya una operacin de escritura en
proceso, ya que la lectura es inmediata, pero la escritura requiere un tiempo para
completarse. Es el propsito del bucle que comienza en la etiqueta noWR, compro-
bando una y otra vez el bit WR de EECON1 hasta que ste vale cero. Una vez ga-
rantizado que no hay ninguna escritura en proceso, el programa carga en el registro
EEADR la direccin de EEPROM en la que escribir. El siguiente paso es cargar en
EEDATA el valor a almacenar en la EEPROM. Tras estos dos pasos viene la activa-
cin de acceso a memoria especial y la seleccin de memoria EEPROM, instruccio-
nes que tambin aparecen en el cdigo para hacer una lectura de EEPROM. Slo
queda habilitar la escritura antes de proceder a la misma. A continuacin viene la
secuencia de cuatro instrucciones obligatorias para quitar el seguro de la operacin
de lectura, y la puesta a uno del bit WR de EECON1, que es el que da comienzo la
Compilador de C para el microcontrolador Microchip PIC18F4550 39
escritura en memoria EEPROM del dato cargado en EEDATA. Antes de terminar,
este cdigo vuelve a poner a cero el bit WREN, evitando con esto posibles escrituras
accidentales.
3.6.7. Memoria Flash
El manejo de la memoria ash de programa comparte algunos registros con el de
la EEPROM, as como los mecanismos de proteccin contra escritura y borrado.
Los registros de control comunes con la EEPROM son EECON1 y EECON2. Aparte
de estos dos, los registros de acceso a tablas TABLAT y TBLPTR tambin son un
componente esencial del mecanismo de acceso a la ash.
Los bits de EECON1 tienen exactamente el mismo propsito que en el caso de
accesos a EEPROM, salvo el bit 4, que slo sirve para habilitar la operacin borra-
do de una la de memoria ash, y los bits 0 y 1, que no cumplen ninguna funcin
cuando se trata de accesos a la memoria ash de programa. De nuevo, el cometido
de EECON2 se reduce a hacer de seguro antes de una operacin de escritura o bo-
rrado, con la misma secuencia de escrituras (0x55h, 0xAAh) inmediatamente antes
de activar el bit WR de EECON1.
A pesar de que el espacio de memoria ash de programa tiene un ancho de pala-
bra de 16 bits, los accesos a ste desde el programa del PIC slo permiten palabras
de 8 bits (ancho de palabra de la memoria datos). El latch que almacena el dato a
intercambiar con la ash es TABLAT. Por otro lado, la funcin de direccionamiento
recae sobre el registro TBLPTR, de 22 bits. Este registro est compuesto por tres SFR:
TBLPTRU (byte superior), TBLPTRH (byte alto) y TBLPTRL (byte bajo).
Un ejemplo de cdigo para leer una palabra (16 bits) de la memoria ash es el
siguiente:
; TBLPTRU[5:0] <- dir[21:16]
MOVLW dirU
MOVWF TBLPTRU
; TBLPTRH[7:0] <- dir[15:8]
MOVLW dirH
MOVWF TBLPTRH
; TBLPTRL[7:0] <- dir[7:0]
MOVLW dirL
MOVWF TBLPTRL
; Cargar en TABLAT el byte bajo
Compilador de C para el microcontrolador Microchip PIC18F4550 40
; e incrementar TBLPTR.
TBLRD
*
+
; Almacenar en memoria de datos.
MOVFF TABLAT, varL
; Cargar en TABLAT el byte alto.
TBLRD
*
; Almacenar en memoria de datos.
MOVFF TABLAT, varH
El procedimiento para escribir en ash es mucho ms complicado que el de lec-
tura debido a algunas caractersticas de la memoria ash, como el tamao de bloque
para escritura (16 palabras, 32 bytes), el tamao de la para borrado (32 palabras,
64 bytes) o la particularidad de que una escritura realmente slo escriba ceros y que
el borrado de un bloque suponga escribir todos sus bytes con 0xFFh. Por este moti-
vo no es extrao combinar una operacin de escritura con un borrado previo y una
copia en memoria de datos del bloque de 64 bytes de ash que resultar sobreescrito.
La mejor manera de comprender esto es con un ejemplo completo, que inclu-
ya copia en memoria de datos de la la a sobreescribir, modicacin de los bytes
deseados, borrado de la la de ash y escritura, en dos iteraciones, de la misma
con los nuevos valores. El siguiente cdigo reemplaza una palabra de dos bytes en
memoria ash, reemplazando toda una la de 64 bytes:
; Bloque de borrado de 64 bytes.
MOVLW D64
MOVWF Contador
; Cargar puntero a buffer auxiliar.
MOVLW BufferDirH
MOVWF FSR0H
MOVLW BufferDirL
MOVWF FSR0L
; Cargar puntero a bloque de
; memoria Flash.
MOVLW FlashDirU
MOVWF TBLPTRU
MOVLW FlashDirH
MOVWF TBLPTRH
Compilador de C para el microcontrolador Microchip PIC18F4550 41
MOVLW FlashDirL
MOVWF TBLPTRL
LEER_BLOQUE:
; Cargar byte en TABLAT e incrementar TBLPTR.
TBLRD
*
+
; Almacenar en buffer y apuntar a la
; siguiente posicin.
MOVFF TABLAT, POSTINC0
; Si terminado (contador == 0), salir del bucle.
; Si no, siguiente iteracin.
DECFSZ Contador
BRA LEER_BLOQUE
MODIFICAR_PALABRA
; Reemplazar byte bajo del dato en el buffer.
MOVLW NuevoDatoL
MOVWF BuffDatoDirH
; Reemplazar byte alto.
MOVLW NuevoDatoH
MOVWF BuffDatoDirL
BORRAR_BLOQUE
; Cargar de nuevo puntero a bloque de
; memoria Flash.
MOVLW FlashDirU
MOVWF TBLPTRU
MOVLW FlashDirH
MOVWF TBLPTRH
MOVLW FlashDirL
MOVWF TBLPTRL
; Seleccionar Flash de programa.
BSF EECON1, EEPGD
; Activar acceso a memoria especial.
BCF EECON1, CFGS
Compilador de C para el microcontrolador Microchip PIC18F4550 42
; Habilitar las operaciones de escritura.
BSF EECON1, WREN
; Habilitar operacin de borrado de fila.
BSF EECON1, FREE
; Deshabilitar las interrupciones.
BCF INTCON, GIE
; Paso 1: EECON2 <- 0x55h
MOVLW 0x55
MOVWF EECON2
; Paso 2: EECON2 <- 0xAAh
MOVLW 0xAA
MOVWF EECON2
; Comenzar borrado (CPU en espera).
BSF EECON1, WR
; Volver a habilitar las interrupciones.
BSF INTCON, GIE
; Cargar de nuevo el puntero a buffer
; auxiliar.
MOVLW BufferDirH
MOVWF FSR0H
MOVLW BufferDirL
MOVWF FSR0L
; Nmero de bloques de escritura: 2
MOVLW D2
MOVWF Bloque
ESCRIBIR_BUFFER
; Bloque de escritura de 32 bytes.
MOVLW D32
MOVWF Contador
ESCRIBIR_BYTE
Compilador de C para el microcontrolador Microchip PIC18F4550 43
; Copiar byte bajo en latch de tabla.
MOVFF POSTINC0, TABLAT
; Escritura "corta" en flash
; (registros de "hold")
TBLWT
*
+
; Si terminado, salir del bucle;
; Si no, siguiente iteracin.
DECFSZ Contador
BRA ESCRIBIR_BYTE
ESCRITURA_LARGA
; Seleccionar Flash de programa.
BSF EECON1, EEPGD
; Activar acceso a memoria especial.
BCF EECON1, CFGS
; Habilitar las operaciones de escritura.
BSF EECON1, WREN
; Deshabilitar las interrupciones.
BCF INTCON, GIE
; Paso 1: EECON2 <- 0x55h
MOVLW 0x55
MOVWF EECON2
; Paso 2: EECON2 <- 0xAAh
MOVLW 0xAA
MOVWF EECON2
; Comenzar escritura (CPU en espera).
BSF EECON1, WR
; Volver a habilitar las interrupciones.
BSF INTCON, GIE
; Si ltimo bloque, salir del bucle;
; Si no, siguiente bloque.
DECFSZ Bloque
BRA ESCRIBIR_BUFFER
Compilador de C para el microcontrolador Microchip PIC18F4550 44
; Volver a deshabilitar la escritura.
BCF EECON1, WREN
La operacin de escritura en ash tiene otra diferencia ms respecto a la misma
sobre EEPROM: el microcontrolador suspende la ejecucin de instrucciones hasta
que naliza la operacin de escritura en memoria ash. Una vez terminada sta,
reanuda la ejecucin de instrucciones y atiende las interrupciones en caso de que
hubiese alguna activa.
La posibilidad de escribir en la memoria ash, ms all de almacenar datos en el
espacio de memoria de programa, permite modicar parte del programa durante la
ejecucin del mismo para poder ejecutar cdigo nuevo no includo en el momento
de programar del microcontrolador.
3.6.8. Timers
Los timers (temporizadores) son dispositivos cuya funcin se limita a contar. De
forma general, tienen dos modos de funcionamiento: reloj (incrementa un valor con
cada ciclo de instruccin) y contador (el incremento se produce con un anco de
subida o bajada de un cierto pin del microcontrolador).
El 18F4550 incorpora cuatro timers, cada uno con caractersiticas propias. Todos
ellos ofrecen la posibilidad de generar interrupciones cuando llegan al desborda-
miento, es decir, cuando incrementan el valor mximo y pasan al mnimo.
De manera resumida, stas son las caractersticas propias de cada temporizador:
El Timer0 puede funcionar en dos modos: reloj o contador. Ambos modos de
funcionamiento pueden emplear palabras de cuenta de 8 y 16 bits. Como reloj,
incrementa el contenido del par de registros TMR0H:TMR0L (slo TMR0L si
est trabajando en modo de 8 bits) con cada ciclo de instruccin (4 ciclos de
reloj). En modo contador, incrementa TMR0 con cada anco de subida (o ba-
jada, segn est congurado) del pin RA4/T0CKI. En ambos casos es posible
usar un preescalado que divida la frecuencia de incremento.
Como hace mencin el prrafo anterior, son los registros TMR0H y TMR0L
los que almacenan la palabra de cuenta. Estos registros permiten lectura y
escritura, con la particularidad de que una operacin de escritura no tiene
efecto hasta dos ciclos de instruccin despus, por motivos de sincronizacin
de cuenta.
La conguracin de este timer recae sobre el registro T0CON. El bit TMR0ON
activa o detiene la cuenta. T08BIT selecciona entre los modos de cuenta de
Compilador de C para el microcontrolador Microchip PIC18F4550 45
8 y 16 bits. T0CS selecciona qu fuente de reloj emplear, cambiando de esta
forma entre reloj interno (reloj de ciclos de instruccin) y contador (fuente de
reloj externa, pin RA4/T0CKI). El pin PSA activa el preescalado cuando est
puesto a 0. El valor de la divisin de frecuencia viene establecido por los bits
T0PS[2:0].
El Timer1 ofrece las mismas posibilidades que el anterior, salvo que slo ad-
mite palabras de cuenta de 16 bits, permite utilizar como seal de reloj un
oscilador de cristal (comn a Timer3, soporta modo asncrono cuando est se-
leccionada la fuente de reloj externa y, adems, es posible congurarlo para
que reinicie la cuenta cuando alguno de los mdulos comparadores (CCP, tra-
tados en la seccin siguiente) active un disparador de evento especial. Asimis-
mo, este temporizador permite emplear como seal de reloj su propio oscila-
dor interno, compuesto por un cristal de cuarzo externo conectado a los pines
RC0/T1OSO/T13CKI y RC1/T1OSI.
T1CON es el registro de conguracin del Timer1. TMR1ON activa el funcio-
namiento del temporizador. TMR1CS selecciona entre el reloj de ciclos de ins-
truccin y una fuente externa. T1SYNC puesto a 0 indica que la cuenta debe
ser sncrona con la entrada de reloj externa. T1OSCEN es el bit de activacin
del oscilador de cristal del Timer1. Los bits T1CKPS[1:0] seleccionan la prees-
cala de reloj a emplear; si estn puestos a 00, no efecta preescalado.
El Timer2 es un temporizador de 8 bits con preescala y postescala. Slo per-
mite utilizar el reloj de ciclos de instruccin como seal de reloj. Incrementa el
valor de TMR2, con cada anco de subida del reloj. Cuando el valor de TMR2
coincide con el del registro PR2, genera la seal de salida del temporizador e
incrementa en uno el valor del contador de postescalado.
El control de este tercer temporizador recae sobre el registro T2CON. TMR2ON
pone en marcha el contador. Los bits T2CKPS[1:0] son los que controlan la
preescala de la cuenta, mientras que T2OUTPS[3:0] hacen lo propio con el va-
lor de postescalado.
El Timer3 es idntico al Timer1. Ambos son temporizadores/contadores de 16
bits que pueden emplear la seal del reloj de ciclos de instruccin, los ancos
de subida de una seal externa o del oscilador de cristal comn, y efectuar
la cuenta de manera sncrona o asncrona a la seal de reloj seleccionada. El
registro de cuenta de este temporizador es TMR3 y, al igual que con los otros
tres temporizadores, permite lectura y escritura.
Compilador de C para el microcontrolador Microchip PIC18F4550 46
El registro de control de Timer3 es T3CON. TMR3ON pone en marcha el con-
tador. TMR3CS selecciona entre el reloj de ciclos de instruccin y una fuen-
te externa. T3SYNC puesto a 0 indica que la cuenta debe ser sncrona con la
entrada de reloj externa. Los bits T1CKPS[1:0] seleccionan la preescala de re-
loj a emplear; si estn puestos a 00, no efecta preescalado. Adems, el bit
T1OSCEN del registro de control del Timer1 tambin forma parte del control
del Timer3, dado que es el que activa el oscilador de cristal, compartido entre
ambos temporizadores.
3.6.9. Capturador, comparador y modulador de ancho pulso
Los microcontroladores PIC18F4550 incorporan dos mdulos CCP, los cuales tie-
nen tres modos de funcionamiento, como su nombre indica: captura, comparacin
y modulacin de ancho de pulso (PWM). Los tres modos trabajan con valores de
16 bits. Los CCP trabajan asociados a un temporizador segn el modo de funcio-
namiento congurado; los modos de captura y comparacin pueden depender del
Timer1 o del Timer3, mientras que el modo PWM slo permite usar el Timer2.
El registro de control de los CCP es CCPxCON. Los bits CCPxM[3:0] seleccionan
qu modo de funcionamiento tendr el CCP. Adems de estos cuatro, los bits 5 y 4
de CCPxCON, denominados DCxB[1:0], son los bits de menor peso que indican la
duracin del pulso activo cuando este CCP est congurado como modulador de
ancho de pulso.
En modo capturador, el CCP captura el valor del temporizador asociado en el
momento que en detecta un evento en su pin CCPx de entrada. Este evento puede
ser un anco de bajada, de subida, o el cuarto o dcimosexto anco de subida de
la seal de este pin. Hay que tener en cuenta que el temporizador asignado a un
CCP en modo captura debe estar congurado como temporizador o como contador
sncrono; de lo contrario el CCP no har su trabajo.
Como comparador, el CCP compara continuamente el valor del temporizador
asignado con el del par de registros CCPRxH:CCPRxL. Cuando los valores coinci-
den, produce un evento programado previamente, que puede ser poner a uno o a
cero el pin de salida CCPx, conmutar el valor de este mismo pin, generar una in-
terrupcin software o disparar un evento especial que acte sobre otros mdulos,
como por ejemplo el temporizador o el conversor A/D.
Como modulador de ancho de pulso, el CCP produce una onda cuadrada cuyo
periodo viene indicado por el registro PR2 y cuya duracin del pulso activo (alto)
es escrita en el registro CCPRxL y los bits CCPxCON[5:4], siendo estos ltimos los
bits menos signicativos de este valor. Como se ve, es un valor de 10 bits, por lo que
Compilador de C para el microcontrolador Microchip PIC18F4550 47
la palabra total es CCPRxL:CCPxCON[5:4]. Las frmulas para calcular el periodo y
ancho activo son las siguientes:
Periodo = (PR2 + 1) 4 T
OSC
(Preescala)
Pulso = CCPRxL : CCPxCON[5 : 4] T
OSC
(Preescala)
3.6.10. Conversor A/D
El mdulo de conversin analgico-a-digital permite la conversin de un valor anal-
gico a uno digital de 10 bits, tomando un valor analgico de referencia como m-
ximo y otro como mnimo. Tiene trece canales de entrada en los pines AN[12:0],
los cuales estn multiplexados sobre la entrada del conversor A/D. El valor de re-
ferencia mximo puede ser la tensin de alimentacin positiva V
DD
o la tensin
aplicada al pin RA3/AN3/V
REF+
. De manera anloga, el valor de referencia m-
nimo puede ser la tensin de alimentacin negativa V
REF
o la aplicada al pin
RA2/AN2/V
REF
. Este mdulo permite, asimismo, la posibilidad de operar mien-
tras el 18F4550 se encuentra en modo de reposo, con la limitacin de que, para
hacerlo, el reloj de conversin A/D debe derivar del oscilador RC interno del con-
versor.
La conguracin del conversor A/D recae sobre tres registros: ADCON0, AD-
CON1 y ADCON2. El registro de control ADCON0 contiene los bits de seleccin de
canal, CHS[3:0]. Adems es estos, incorpora el bit de estado de la conversin, GO,
que inicia el proceso de conversin y permanecer puesto a uno mientras sta no
haya terminado; una vez completada la conversin, este bit pasa automticamente
a valer cero. El bit ADON es el que habilita o deshabilita el mdulo conversor. El
registro ADCON1 contiene los bits para conguracin de la tensin de referencia,
VCFG1 para la referencia negativa y VCFG0 para la positiva, as como los bits de
conguracin de puertos A/D, PCFG[3:0]. Por ltimo, ADCON2 contiene los bits
de seleccin de reloj para la conversin A/D, ADCS[2:0]; los bits de seleccin de
tiempo de adquisicin, ACQT[2:0]; y el bit de formato para el resultado, ADFM,
que indica si el resultado estar justicado a izquierda o derecha.
Los registros donde el conversor almacena el resultado de la conversin son
ADRESH y ADRESL. El resultado puede estar en ADRESH[1:0]:ADRESL[7:0] o, por
el contrario, ADRESH[7:0]:ADRESL[7:6], segn indique el bit ADFM. El resto de bits
valdrn cero.
Los pasos necesarios para leer un valor analgico utilizando el mdulo A/D
Compilador de C para el microcontrolador Microchip PIC18F4550 48
pueden resumirse en los siguientes:
1. Congurar pines A/D para la entrada deseada.
2. Seleccionar tensiones de referencia.
3. Seleccionar fuente de reloj.
4. Seleccionar canal de entrada.
5. Habilitar mdulo A/D.
6. Iniciar conversin (GO <- 1).
7. Espera activa mientras GO=1.
8. Resultado almacenado en ADRESH:ADRESL.
Hay que aadir una precaucin muy importante a la hora de efectuar el procedi-
miento descrito. La conversin A/D no puede activarse justo despus de habilitar
el mdulo conversor. Es necesario esperar un tiempo para dejar que el condensa-
dor de mantenimiento, C
HOLD
, alcance la tensin que hay aplicada en el canal de
entrada analgica. Este tiempo recibe el nombre de tiempo de adquisicin, T
ACQ
, y
es la suma del tiempo de establecimiento del amplicador de entrada, el tiempo de
carga de C
HOLD
y un coeciente que depende de la temperatura. En el caso de em-
plear una impedancia de la fuente R
S
de 2.5K, valor mximo recomendado para
fuentes analgicas, un C
HOLD
de 25pF, y que la temperatura mxima de funciona-
miento sea de 85

C, el tiempo de adquisicin es de 2.45s. Por consiguiente, antes


de activar el bit GO de ADCON0 es necesario garantizar que han transcurrido al
menos 2.45s desde que se habilit el mdulo conversor A/D, o de lo contrario la
conversin resultar en un valor incorrecto.
Otra restriccin de tiempo es el tiempo de conversin por bit, denominado T
AD
.
El fabricante Microchip garantiza una conversin correcta para tiempos de conver-
sin entre 1.4s y 25s. Por otro lado, el conversor del 18F4550 necesita 11 T
AD
para completar una conversin de 10 bits, entre 15.4 y 275s, por lo tanto es nece-
sario seleccionar un T
AD
comprendido en este rango de valores. El mdulo A/D
ofrece siete opciones posibles: 2*T
OSC
, 4*T
OSC
, 8*T
OSC
, 16*T
OSC
, 32*T
OSC
, 64*T
OSC
y el oscilador interno RC, cuyo periodo tpico es de 2.5s. El cuadro 3.6 muestra
los tiempos de las fuentes de reloj para distintas frecuencias de funcionamiento del
microcontrolador.
Dado que el tiempo mnimo de T
AD
es de 1.4s, los valores menores (entre pa-
rntesis en la tabla) no permiten una conversin correcta. La mejor opcin es elegir
Compilador de C para el microcontrolador Microchip PIC18F4550 49
Fuente de reloj
ADCS[2:0] 20 MHz 4 MHz 1 MHz
(valor de T
AD
)
2*T
OSC
000 (0.1s) (0.5s) 2s
4*T
OSC
100 (0.2s) (1s) 4s
8*T
OSC
001 (0.4s) 2s 8s
16*T
OSC
101 (0.8s) 4s 16s
32*T
OSC
010 1.6s 8s 32s
64*T
OSC
110 3.2s 16s 64s
RC x11 2-6s 2-6s 2-6s
Cuadro 3.6: Tiempos segn fuente de reloj de T
AD
en funcin de la frecuencia del
microcontrolador
la fuente que ofrezca el menor tiempo de conversin posible y que ste no sea infe-
rior al mnimo impuesto por el hardware. Por ejemplo, para el caso de un sistema con
un reloj de cristal de 4MHz es posible elegir cinco conguraciones posibles, 8*T
OSC
,
16*T
OSC
, 32*T
OSC
, 64*T
OSC
y RC, dado que las dos opciones restantes no aseguran
un tiempo de conversin superior al mnimo de 1.4s. De estas cinco opciones, lo
ms adecuado en la mayora de los casos ser escoger 8*T
OSC
, que es la fuente de re-
loj que produce el T
AD
vlido ms pequeo. No obstante, es posible escoger 8*T
OSC
,
o cualquier otra fuente de periodo mayor, la cual tambin llevara a una conversin
A/D correcta pero con un mayor gasto de tiempo.
3.6.11. Comparador Analgico
El mdulo comparador analgico incluye dos comparadores cuyas entradas y
salidas permiten ocho conguraciones diferentes, desde estar todas desconectadas,
dejando desactivados ambos comparadores, hasta modos complejos, como el de
cuatro entradas multiplexadas dos a dos. Por supuesto, tambin es posible la con-
guracin ms obvia de dos comparadores independientes.
El registro CMCON es el que dicta qu conguracin emplear este mdulo. La
seleccin entre los ocho modos de funcionamiento es indicada por los bits CM[2:0].
Los bits C1OUT, C2OUT, C1INV y C2INV permiten controlar la lgica de compa-
racin y salida de cada comparador por separado. Este registro tambin incluye un
bit para controlar el multiplexor de las entradas cuando el mdulo est congurado
para entradas multiplexadas.
Compilador de C para el microcontrolador Microchip PIC18F4550 50
3.6.12. Mdulo de Tensin de Referencia
El propsito de este mdulo es generar una tensin exacta que pueda utilizarse
como referencia por el propio microcontrolador u otros dispositivos que requieran
una tensin de referencia exacta. La ventaja de disponer de un mdulo como ste
incorporado en el microcontrolador es, adems de eliminar la necesidad de circui-
tera externa, la posibilidad de utilizarlo como referencia para varios dispositivos
cuyo funcionamiento no se solape en el tiempo.
La tensin generada por este dispositivo puede ser relativa a V
DD
, a V
SS
o a una
tensin de referencia externa, con lo que la exibilidad que otorga este mdulo no
deja lugar a dudas.
Al igual que ocurre con el comparador analgico, un nico registro de con-
trol es suciente para albergar toda la conguracin. El nombre de este registro
es CVRCON.
3.6.13. Otros
Adems de los dispositivos ya vistos, los microcontroladores pueden incorporar
perifricos de otro tipo. Debido a la gran diversidad de perifricos existentes no es
posible disponer de todos ellos en un modelo nico de microcontrolador.
Algunos de los dispositivos que es posible encontrar en otros modelos de micro-
controlador, aparte de los ya descritos para el PIC18F4550, son:
Conversor A/D de rampa. Es un modelo de conversor analgico-a-digital di-
ferente al que incorpora el 18F4550. En lugar de efectuar un bucle de compa-
racin y aproximaciones sucesivas, este dispositivo mide el tiempo de carga
de un condensador y, a partir de ah, calcula el valor de la tensin aplicada a
la entrada (bornas del condensador). Este tipo de conversores son ms lentos
que los basados en el mtodo de aproximaciones sucesivas pero tambin son
ms precisos.
Mdulo LCD. Este tipo de mdulos facilita el manejo de pantallas LCD desde
un PIC. Generalmente, su uso se reduce a congurar el tiempo de refresco y
denir los registros que servirn como memoria grca. Una vez hecha la con-
guracin, slo hay que escribir en la memoria grca los datos que debern
aparecer en la pantalla.
Compilador de C para el microcontrolador Microchip PIC18F4550 51
3.7. Caractersticas especiales de la CPU
3.7.1. Interrupciones
El PIC18F4550 tiene hasta 19 fuentes de interrupcin, 16 generadas por los peri-
fricos integrados y 3 seales de interrupcin entrantes desde dispositivos externos.
Por supuesto, no es necesario que todas ellas estn habilitadas. Adems, es posible
agrupar las interrupciones en dos niveles de prioridad, alta o baja, o conservar el
modo de compatibilidad en el que todas las interrupciones tienen la misma prioridad,
tal como sucede en los PIC de gama media.
Diez registros son los responsables del control de interrupciones en este micro-
controlador:
RCON
INTCON
INTCON2
INTCON3
PIR1 y PIR2
PIE1 y PIE2
IPR1 y IPR2
Cada fuente de interrupcin tiene tres bits que controlan su funcionamiento in-
dividual:
Bit de activacin o Flag bit. Indica que se produjo la interrupcin.
Bit de habilitacin. Si est activado, la CPU ejecutar la rutina de tratamiento de
interrupcin (ISR) correspondiente cuando sta interrupcin se produzca.
Bit de prioridad. Selecciona a qu nivel de prioridad pertenece esta interrup-
cin.
Existen dos vectores de interrupcin cuando la prioridad de interrupcines est
habilitada. El vector que da servicio a las interrupciones de alta prioridad se en-
cuentra en la posicin 0x000008h de la memoria de programa. Por otro lado, la di-
reccin donde se aloja el vector asociado a las interrupciones de prioridad baja es
0x000018h.
Compilador de C para el microcontrolador Microchip PIC18F4550 52
A pesar de disponer de dos vectores de interrupcin, la gestin de interrupcio-
nes no es vectorizada: es necesario efectuar un polling por software para determinar
qu dispositivo fue el que activ la seal de interrupcin.
Cada rutina de tratamiento de interrupcin debe nalizar con la instruccin
RETFIE, que recupera de la pila el valor de PC en el momento de producirse la inte-
rrupcin. Es responsabilidad del programador incorporar el cdigo adecuado para
salvaguardar los valores de los registros modicados durante la rutina de atencin,
includo el registro WREG, para restaurarlos antes de ejecutar la instruccin RET-
FIE.
El registro de control INTCON alberga dos de los tres bits ms importantes del
control general de interrupciones: GIE/GIEH y PEIE/GIEL. El primero de ellos ha-
bilita las interrupciones de prioridad alta, o todas las interrupciones si el controla-
dor de interrupciones est puesto en modo de compatibilidad. El segundo bit habilita
las interrupciones de prioridad baja, salvo en modo de compatibilidad, cuya funcin
es habilitar las interrupciones generadas por los perifricos integrados en el PIC.
El tercer bit esencial para control general del controlador de interrupciones es el
octavo bit del registro RCON, PIEN, que habilita los niveles de prioridad de las
interrupciones.
Aparte de los ya descritos, el registro INTCON, junto con INTCON2 e INTCON3,
alberga los bits de habilitacin, activacin y prioridad de las entradas de interrup-
cin externas, el Timer0 y el puerto B.
Los registros PIE1 y PIE2 alojan los bits de habilitacin de las interrupciones
generadas por los perifricos del PIC. PIR1 y PIR2, por su parte, estn compuestos
por los bits de activacin de interrupciones, mientras que IPR1 e IPR2 contienen los
bits de prioridad de cada perifrico.
Hasta este momento, la nica manera de controlar dispositivos era programarlos
para que comenzasen a funcionar y efectuar una espera activa, comprobando una y
otra vez los bits de estado, hasta que el trabajo hubiese terminado. Ahora, haciendo
uso del controlador de interrupciones es posible congurar un perifrico, activar la
interrupcin adecuada y ponerlo a funcionar mientras la CPU efecta otras tareas,
con la seguridad de que cuando el perifrico haya completado su trabajo, la inte-
rrupcin captar la atencin del microcontrolador. De esta manera, el tiempo que
antes era desperdiciado en la espera activa ahora pasa a ser tiempo invertido en otras
tareas ms provechosas. Por ejemplo, para la transmisin del EUSART, es posible
preparar una ISR en la direccin 0x000008h que compruebe si el bit TRMT del re-
gistro TXSTA est puesto a uno y, en caso armativo, ejecute una accin, y activar
la interrupcin de transmisin del EUSART.
Compilador de C para el microcontrolador Microchip PIC18F4550 53
3.7.2. Perro Guardin
El dispositivo perro guardin, ms conocido por sus siglas en ingls WDT (Watch-
Dog Timer), es un temporizador que utiliza el oscilador RC interno del microcontro-
lador. Como el oscilador RC es independiente del reloj utilizado por el micro, este
temporizador sigue contando aunque el PIC est en modo de reposo. La misin del
WDT es producir, cuando llega al nal de su cuenta, el reinicio del microcontrolador
cuando ste se encuentra en modo normal, o reactivarlo si est en modo de reposo.
3.7.3. Modo de reposo
El modo de reposo es un modo donde los osciladores y temporizadores del PIC
estn detenidos, con lo que la ejecucin del programa tambin queda suspendida.
Las nicas excepciones son el oscilador interno RC, en el caso de que el WDT est
activo, y el Timer1, si se encuentra funcionando en el momento de entrar en modo
de reposo.
Los eventos que despiertan al microcontrolador son el desbordamiento del con-
tador del WDT, una seal de interrupcin proveniente de alguno de los perifricos
que no dependen de ningn oscilador distinto al RC, una interrupcin que proven-
ga del exterior, o un reset del sistema.
3.7.4. ICSP
ICSP son las siglas de In-Circuit Serial Programming. El interface ICSP permite
programar el PIC estando ya montado en el circuito de aplicacin nal, sin necesi-
dad de desconectarlo del mismo. Est compuesto por cinco lneas, las cuales deben
cumplir unas restricciones determinadas. Las tensiones de alimentacin V
DD
y ma-
sa V
SS
sern las mismas que las utilizadas para el funcionamiento habitual del PIC.
La tensin de programacin V
PP
ser de al menos 13V; esta tensin aplicada al pin
MCLR del microcontrolador hace que entre en modo programacin. Por ltimo, las
seales de reloj (PGC) y datos (PGD) debern alcanzar los valores de V
PP
y V
SS
3.8. Caractersticas elctricas
De manera general, las caractersticas del PIC18F4550 permiten un amplio rango
de aplicaciones.
Segn el datasheet, este microcontrolador puede funcionar en un rango de tem-
peraturas comprendido entre -40

C y 85

C. Estos valores son los lmites que no


debern superarse durante la operacin del PIC. No cumplir las limitaciones de
Compilador de C para el microcontrolador Microchip PIC18F4550 54
temperatura puede resultar en una ejecucin incorrecta del programa o incluso la
destruccin fsica del dispositivo. Es conveniente recordar que la temperatura no
solo puede daar el microcontrolador, sino que afecta cuantitativamente al com-
portamiento del mismo, pudiendo variar, por poner un ejemplo, la frecuencia de
oscilacin del oscilador empleado por el conversor A/D, dando lugar a un posible
fallo en la conversin.
El fabricante Microchip distribuye series especiales de microcontroladores para
aplicaciones militares, los cuales soportan condiciones de funcionamiento ms ex-
tremas que las series estndar.
El rango de tensiones soportado por cualquier pin comprende aquellas tensio-
nes entre 0.3V por debajo de V
SS
y V
DD
+0.3V por encima de V
SS
. Por ejemplo, si
V
SS
est contectado a 0.2V, una entrada a nivel bajo no deber ser inferior a -0.1V;
suponiendo que V
DD
est conectado a 5V, la tensin aplicada a una entrada para un
nivel alto no deber exceder de 5.5V.
Slo dos pines tienen un rango distinto al descrito en el prrafo anterior: V
DD
y MCLR. El pin de alimentacin V
DD
admite un rango de tensiones que va desde
0.3V por debajo de V
SS
hasta 7.5V por encima. Por otro lado, el rango de tensiones
soportado por MCLR va desde V
SS
hasta 13.25V por encima de ste.
Ntese que estos rangos de tensiones son los valores que admite el microcontro-
lador sin llegar a daarse fsicamente, no los valores necesarios para su buen fun-
cionamiento. Por ejemplo, V
DD
admite desde V
SS
-0.3V hasta V
SS
+7.5V, pero para
funcionar es necesario que V
DD
est comprendido entre 4.2V y 5.5V.
La corriente que puede suministrar o consumir por los pines de entrada y salida
es de 25 mA. Esta es suciente para iluminar leds, y comunicarse con otros inte-
grados. Para poder manejar corrientes mayores es necesario emplear transductores
externos, dado que un exceso de corriente a travs de los pines puede destruir el
microcontrolador.
Compilador de C para el microcontrolador Microchip PIC18F4550 55
Smbolo Pin Mnimo Mximo
V
IL
Pin E/S con buffer TTL V
SS
0.15 V
DD
Pin E/S con buffer Trigger Schmitt V
SS
0.2 V
DD
MCLR V
SS
0.2 V
DD
OSC1 y T1OSI (en modos XT, HS y HSPLL) V
SS
0.3 V
DD
OSC1 (en modo EC) V
SS
0.2 V
DD
Pin RB0 y RB1 (en modo I
2
C) V
SS
0.3 V
DD
V
IH
Pin E/S con buffer TTL 2V V
DD
Pin E/S con buffer Trigger Schmitt 0.8 V
DD
V
DD
MCLR 0.8 V
DD
V
DD
OSC1 y T1OSI (en modos XT, HS y HSPLL) 0.7 V
DD
V
DD
OSC1 (en modo EC) 0.9 V
DD
V
DD
Pin RB0 y RB1 (en modo I
2
C) 0.7 V
DD
V
DD
V
OL
Pin E/S - 0.6V
OSC2/CLKO (en modo EC e ECIO) - 0.6V
V
OH
Pin E/S V
DD
-0.7V -
OSC2/CLKO (en modo EC, ECIO e ECPIO) V
DD
-0.7V -
Cuadro 3.7: Niveles de tensin para el microcontrolador PIC18F4550
Captulo 4
GNU Compiler Collection
4.1. Historia de GCC
Ao 1983, 27 de septiembre. Llega al grupo net.unix-wizards de Usenet un men-
saje que comienza exclamando Unix libre!. El autor de este correo es un joven
de Massachusetts llamado Richard M. Stallman. En l, anunciaba su proyecto de
desarrollar un sistema operativo tipo Unix completamente libre. Har falta ayuda
en forma de tiempo, dinero, programas y equipos. De esta manera naca el proyec-
to GNU, cuyo objetivo era proporcionar un sistema operativo libre para usuarios y
desarrolladores.
Los sistemas Unix estn ntimamente relacionados con el lenguaje C, por lo que
todos ellos disponen de al menos un compilador de este lenguaje. Dado que no
exista ningn compilador libre, Stallman decidi crear uno desde cero que sirviese
para su propsito.
Todo este trabajo fue posible gracias al esfuerzo de muchas personas y la nan-
ciacin tanto de personas individuales como de la Fundacin del Software Libre, ms
conocida por sus siglas en ingls, FSF. Richard Stallman decidi crear la FSF en
1985 con el propsito de proporcionar soporte logstico, nanciero y legal al pro-
yecto GNU.
En el ao 1987 apareci la primera versin del GNU C Compiler. Esta versin
marc un antes y un despus en la historia del software, ya que se trataba del primer
compilador de lenguaje C completamente libre. Desde ese momento GCC es una de
las herramientas de desarrollo de software ms importantes.
La versin 2.0 vi la luz en 1992. Fue la primera gran revisin de GCC, e incor-
por cambios en su arquitectura que permitieron la inclusin de mejoras que eran
imposibles en la rama 1.x.
Durante el tiempo de vida de la rama 2.x la FSF mantuvo un estricto control so-
56
Compilador de C para el microcontrolador Microchip PIC18F4550 57
bre las mejoras de GCC. A muchos desarrolladores les resultaba frustrante lo difcil
que resultaba que la FSF incluyese sus mejoras en la versin ocial del compilador.
Por este motivo, en 1997, un grupo de desarrolladores insatisfechos con el lento
desarrollo de GCC opt por iniciar un proyecto a partir de la versin de desarrollo
del GCC ocial. Este proyecto bifurcado de GCC recibi el nombre de Sistema Com-
pilador de GNU Experimental/Mejorado (Experimenta/Enhanced GNU Compiler System),
EGCS), e incorporaba innidad de mejoras ajenas a las de la FSF y que no haban
sido incluidas en GCC.
El desarrollo de EGCS fue mucho ms rotundo que el de GCC hasta el punto
de que, en 1999, la FSF opt por dejar de desarrollar GCC y bendecir EGCS como
versin ocial de GCC. El primer nuevo GCC ocial fue GCC 2.95.
A da de hoy, GCC ha sido ampliado para soportar muchos lenguajes adiciona-
les, incluyendo C++, Objetive-C, Java y ADA entre muchos otros. Puesto que GCC ya
no es slo un compilador de C, su nombre ha pasado a ser GNU Compiler Collection
(Coleccin de Compiladores de GNU). Las decisiones sobre el camino a seguir en el
desarrollo de GCC, en la actualidad, las toma el GCC Steering Committee, un grupo
formado por desarrolladores e ingenieros de todo el mundo.
4.2. Caractersticas
GCCes un compilador portable. Se ejecuta en la mayora de plataformas actuales
y puede producir cdigo para un gran nmero de arquitecturas. Adems de los
procesadores usados en PCs, soporta microcontroladores, DSPs y CPUs de 64 bits.
GCC no es slo un compilador nativo, puede realizar compilacin cruzada de
cualquier programa, produciendo ejecutables para diferentes sistemas desde cual-
quiera que use GCC. Esto permite compilar software para sistemas empotrados que
no tengan la capacidad de ejecutar el compilador.
GCC esta escrito principalmente en C, con el objetivo de ser portable, y puede
compilarse a si mismo para adaptarse a nuevos sistemas sin dicultad.
GCC tiene un diseo modular que le permite soportar nuevos lenguajes y ar-
quitecturas. Para soportar un nuevo lenguaje es suciente con incluir un front-end
compatible que traduzca dicho lenguaje y aadir las bibliotecas necesarias en tiem-
po de ejecucin para los programas escritos en el lenguaje en cuestin. De manera
anloga, para soportar una nueva arquitectura basta con tener un back-end que pro-
duzca cdigo ensamblador para sta.
En la actualidad, GCC dispone de mltiples front-ends, para traducir diferentes
lenguajes, as como diversos back-ends para soportar mltiples arquitecturas compu-
tacionales diferentes.
Compilador de C para el microcontrolador Microchip PIC18F4550 58
Por ltimo, GCC es software libre, distribuido bajo Licencia Pblica General de
GNU (General Public License, GPL). Esto signica que cualquier persona tiene la li-
bertad de poder usarlo y modicarlo para cualquier propsito. Si se necesita soporte
para una nueva arquitectura, lenguaje, o una caracterstica an no includa, la GPL
garantiza la libertad para incluir dichas modicaciones e incluso publicarlas, con la
nica condicin de conservar la licencia en el software publicado.
El resultado ms destacable de esta libertad es que cualquier persona puede be-
neciarse de las mejoras introducidas por terceras partes. GCC es un buen ejemplo
de cmo el trabajo cooperativo benecia a toda la humanidad.
4.3. Estructura
La interface externa es la propia de cualquier programa de lnea de comandos. El
usuario invoca un programa controlador, llamado gcc, que interpreta los argumen-
tos, decide qu compilador utilizar con cada archivo de entrada, ejecuta el ensambla-
dor para la arquitectura objetivo y, posiblemente, termina ejecutando el programa
enlazador.
Cada uno de los compiladores es un programa independiente que recibe como
entrada uno o varios archivos de cdigo fuente y genera un cdigo en lenguaje
ensamblador. Cada compilador no es ms que una combinacin de mdulos internos
de GCC.
El pipeline de GCC est compuesto, a grandes rasgos, por tres etapas:
Front-end El front-end es la primera etapa del proceso de compilacin. En esta etapa
el cdigo fuente de entrada se convierte en una estructura de rbol sintctico
optimizada segn el lenguaje de origen.
Middle-end Es la etapa intermedia, donde el rbol sintctico recibe optimizaciones
independientes de la arquitectura y el lenguaje, y se transforma en un cdigo
intermedio independiente del lenguaje inicial y de la arquitectura objetivo.
Back-end Esta es la etapa nal. En ella, el cdigo intermedio generado por el middle-
end recibe optimizaciones especcas para la arquitectura objetivo y nalmen-
te se genera el cdigo ensamblador.
4.3.1. Front-end
Los front-end son diferentes para cada lenguaje de entrada, pero todos generan
rboles sintcticos que el middle-end puede manejar. Todos ellos implementan ana-
lizadores gramaticales LALR(1).
Compilador de C para el microcontrolador Microchip PIC18F4550 59
Internamente, el front-end ejecuta la fase de preprocesado, si la hubiese, y efecta
el anlisis sintctico, del que resulta una estructura de rbol sintctico en lenguaje
GENERIC. Tras diversos procesos de optimizacin dependientes del lenguaje de
entrada, este cdigo GENERIC pasa por un proceso de simplicacin, denominado
gimplicacin. Producto de este proceso es la representacin en lenguaje GIMPLE del
cdigo original de entrada. Esta estructura en GIMPLE es la que recibe el middle-end.
4.3.2. Middle-end
El middle-end est compuesto por diversas fases de optimizacin independientes
tanto del lenguaje de entrada como de la arquitectura objetivo. Estas optimizaciones
incluyen desplegado de bucles, propagacin de expresiones constantes, supresin
de la recursin de cola, eliminacin de redundancias, eliminacin de cdigo que
no se ejecuta, optimizacin de bucles y descomposicin de las estructuras corres-
pondientes al manejo de excepciones de alto nivel en sus equivalentes en forma de
sentencias de control de ujo.
Tras todas las fases de optimizacin, el cdigo GIMPLE ya optimizado se con-
vierte al lenguaje de transferencias de registros (RTL) especco de GCC. Este lenguaje,
guarda un gran parecido con el lenguaje Lisp. Es muy expresivo, y ha sido disea-
do para que resulte sumamente fcil traducirlo a cdigo ensamblador de cualquier
procesador, con independencia de la arquitectura de ste.
4.3.3. Back-end
Los back-end son especcos de cada arquitectura. Cada uno produce cdigo en-
samblador para una arquitectura objetivo diferente pero todos ellos toman como
entrada un cdigo RTL, que es independiente de la arquitectura y, por supuesto,
del lenguaje de entrada.
El comportamiento del back-end viene parcialmente especicado por las macros
del preprocesador especcas de la arquitectura objetivo. Por ejemplo, existen ma-
cros para denir el tamao de palabra, la convencin de llamadas a sub-rutina o la
endianness que utilice la mquina que ejecute el programa compilado. El back-end ha-
ce uso de estas macros para adaptar el cdigo RTL inicial a la arquitectura objetivo
y efectuar optimizaciones dependientes de la misma.
Por ltimo, el back-end empareja, una a una, las expresiones RTX del cdigo RTL
con los patrones de cdigo ensamblador dados en la descripcin de mquina co-
rrespondiente a la arquitectura objetivo y concluye el proceso de compilacin con
la sustitucin de cadenas simblicas por direcciones fsicas de memoria o registros
del procesador.
Captulo 5
Diseo de la solucin
Registros, memoria, operaciones matemticas y control de ujo son los cuatro
pilares maestros sobre los que recae el peso de este proyecto. Una vez diseados,
permitirn trabajar de manera coherente con la especicacin de la arquitectura.
El primer paso es denir estos cuatro puntos. Una vez tomadas las decisiones
de diseo fundamentales, la creacin de patrones de instruccin resulta semejante a
programar pequeos fragmentos de cdigo ensamblador para la arquitectura nal.
5.1. Registros
Los PIC tienen un nico registro como tal, conocido como registro de trabajo o
WREG. Una primera solucin es indicar a GCC en la descripcin de la arquitectura
que la mquina objetivo slo dispone de un registro fsico. Teniendo en cuenta que
el compilador hace un uso intensivo de un lenguaje intermedio basado en registros
(RTL) y que utiliza tantos registros
1
como considera necesario, esta idea no parece
una buena solucin. Si se optara por este diseo, los cdigos generados por GCC
resultaran muy inecientes, debido al exceso de operaciones de salvado y recupe-
racin del valor del nico registro disponible.
La segunda idea considerada es diametralmente opuesta a la anterior. Partiendo
de que cada posicin de memoria RAMde un PIC recibe el nombre de registro, GCC
podra considerar que tiene a su disposicin tantos registros como posiciones de me-
moria RAM haya. Sin embargo, el cdigo generado por este compilador, adems de
registros, necesita memoria para almacenar variables y salvaguardar valores interme-
dios, entre otras cosas, y esta solucin implica que no quede RAM disponible para
dedicar como memoria.
1
Estos registros no son registros fsicos del procesador, sino contenedores abstractos de datos que
GCC considera que tienen las caractersticas de registros fsicos.
60
Compilador de C para el microcontrolador Microchip PIC18F4550 61
Antes de optar por una u otra solucin, hagamos una breve estimacin del uso
de los registros. Para sumar dos valores enteros, de 16 bits, harn falta seis registros,
de ocho bits: dos registros cada sumando y dos ms para almacenar el resultado. En
el caso de que la operacin de suma sobreescriba uno de los dos operandos, slo
sern necesarios cuatro registros. La operacin de suma de dos nmeros reales de 32
bits cada uno emplea, en el caso ms conservador que no conlleve sobreescritura,
un total de doce registros; ocho si uno de los sumandos resulta sobreescrito por el
resultado. Hasta ahora parece razonable considerar un mnimo de ocho registros,
lo que descarta por completo la primera solucin.
Por otro lado, el grueso de operaciones, tal como las ve el back-end, operan con
registros; lo que puede llevar a pensar que la segunda solucin, que considera to-
da la memoria fsica del microcontrolador como registros, es la ms adecuada. Sin
embargo, esta solucin tambin resulta inviable debido a la necesidad de memoria
aparte de los registros, de lo que deriva la idea de establecer un lmite superior en el
nmero de registros fsicos que GCC puede utilizar.
Planteadas estas restricciones, la mejor solucin es un compromiso entre el lmi-
te inferior de ocho regisros y el superior, que no deja RAM libre para que el com-
pilador considere memoria. El registro fsico WREG ser transparente para GCC, de
modo que, a pesar de que el cdigo nal en ensamblador lo utilizar de manera
intensiva, el compilador no tendr en cuenta su existencia. As, la solucin nal al
problema de los registros utilizar un mximo de 32 registros, nmero que deja su-
cientes posiciones de RAM disponibles para otros usos y es lo bastante alto como
para permitir una optimizacin de operaciones consecutivas ms que razonable.
5.2. Memoria
GCC accede a memoria
2
mediante direccionamiento indirecto, haciendo uso de
punteros almacenados en registros. El principal puntero a memoria es el frame pointer.
El propsito de este puntero es direccionar el marco de memoria donde se almace-
nan las variables locales de una funcin.
La memoria, al igual que los registros, para GCC ser una porcin de la RAM del
PIC. Adems de registros y memoria, habr un espacio en RAM asignado para la pila
virtual y varios registros de propsito especco para el control del programa. Tras
eliminar estas porciones de RAM asignadas de antemano, el espacio restante ser
asignado a memoria.
2
Lo que GCC considera memoria, en el caso de la arquitectura PIC y nuestro back-end, es el mismo
espacio de memoria de datos que el utilizado para lo que GCC considera registros, aunque se trate
de porciones disjuntas de la memoria de datos del PIC.
Compilador de C para el microcontrolador Microchip PIC18F4550 62
La arquitectura del PIC18 divide el espacio de memoria de datos en bancos de
256 bytes, a los que hay que restar el espacio designado para gestin de memoria,
por lo que resulta un mximo de 252 bytes por cada banco, con la excepcin del
banco 0, cuyas posiciones inferiores corresponden tambin al banco de acceso. No
es posible designar el espacio asignado a memoria por el compilador, ya que ste
depende de la asignacin de RAM a espacio de pila y control de programa. Slo
existe la seguridad de que el mximo es de 252 bytes por banco, un total de 1768
bytes en el PIC18F4550.
Cuando GCC traduce una llamada a una funcin, conoce de antemano la can-
tidad de memoria necesaria para almacenar las variables empleadas por la misma.
Esta memoria es la denominada marco (en ingls frame) de la funcin. Una vez co-
nocido este detalle estamos en posicin de armar que una buena estrategia de
seleccin de banco hara ms eciente el uso de la RAM por parte de los progra-
mas compilados con GCC. Sin embargo, a pesar de escoger una estrategia ptima,
siempre existe la posibilidad de que ninguno de los bancos disponga de sucientes
posiciones libres para almacenar el marco de una funcin concreta. Llegado a este
caso, la nica solucin es redisear el programa que se quiere compilar.
La solucin tomada para la asignacin de RAM como memoria emplea dos pala-
bras de memoria RAMpor cada banco para almacenar el tamao de ste y el espacio
utilizado del mismo hasta el momento. Al invocar una funcin, busca cual de los
bancos dispone de espacio suciente para albergar el marco de la funcin, efectuan-
do la diferencia entre espacio total y espacio ocupado. En caso de que alguno de los
bancos satisfaga los requisitos de espacio libre, actualiza el espacio usado e iniciali-
za el puntero a marco de funcin (frame pointer) para que apunte al inicio del espacio
reservado en ese banco. Al nalizar la funcin actualiza la variable de espacio usa-
do del banco. Teniendo en cuenta que con cada llamada a funcin se pierde el frame
pointer correspondiente al marco de la funcin invocadora, es necesario salvar el
mismo cada vez que se efecte una llamada a funcin y restaurarlo al salir de cada
funcin invocada. Para esto se emplea la pila virtual.
GCC est diseado de forma que minimice los accesos a memoria. Siempre que le
sea posible, mantendr los valores de variables locales en registros y no en memoria,
ahorrando de este modo los costosos accesos a memoria y, en caso de conseguirlo
con todas las variables, la reserva de espacio.
5.3. Paso de argumentos a funciones
En el paso de argumentos a funciones es posible clasicar los datos en dos tipos:
simples y compuestos. Los tipos de datos simples son los caracteres, y los valores
Compilador de C para el microcontrolador Microchip PIC18F4550 63
enteros y reales, mientras que los datos compuestos son los vectores, estructuras y
uniones.
Los argumentos que sean datos simples, por su tamao reducido y su naturale-
za sencilla se pasarn almacenados en registros. La asignacin de registros para tal
efecto comenzar por el ltimo registro y el almacenamiento ser en orden inverso,
correspondiendo el primer dato al ltimo registro; el segundo, al penltimo y as su-
cesivamente. De esta forma la posibilidad de solapamiento entre registros para uso
general y registros designados para paso de argumentos se ve reducida notablemen-
te. A modo de ejemplo, supongamos que una funcin toma dos argumentos, uno
de tipo char y otro de tipo int. Segn esta solucin, el registro nmero 32 albergar
el carcter, mientras que el registro 31 almacenar el byte bajo del entero, y el 30
har lo propio con el byte alto del mismo parmetro.
El paso de argumentos de tipo compuesto ser siempre por referencia. En lugar
del valor almacenado en registros, la funcin recibir un puntero a la direccin de
memoria donde est alojado el primer byte del dato pasado como argumento. As,
un vector de n elementos pasado como parmetro slo ocupar dos registros de la
lista de argumentos de la funcin en cuestin.
5.4. Retorno de valores de funciones
La solucin ideada para el retorno de valores es semejante al paso de argumen-
tos, con la diferencia de que la asignacin de registros destinados a este propsito
comienza en la mitad de la lista de registros y contina en sentido ascendente. De
este modo, el dato devuelto por una funcin estar almacenado a partir del registro
16.
Igual que sucede con el paso de argumentos a una funcin, el retorno de valores
devueltos distingue dos casos segn la clase del dato devuelto. Para datos simples,
el valor devuelto ser alojado en registros a partir de la mitad de la tabla de registros
posibles. En cambio, cuando una funcin devuelve un valor de tipo compuesto la
solucin no es tan sencilla.
El retorno de valores de tipo compuesto ser por referencia, en lugar de por
valor. Esta decisin resulta en un cdigo ms eciente, ya que ahorra el tener que
duplicar un valor complejo en el momento de nalizar una funcin. No obstante, es-
ta mejora no es gratuita: para que la referencia sea accesible por la funcin llamante
es necesario pasar un argumento extra a la funcin llamada que ser un apuntador
a la posicin de memoria donde esperamos encontrar el dato resultante una vez
completada la ejecucin de la funcin invocada. El valor devuelto por la funcin
ser un puntero a los datos del resultado.
Compilador de C para el microcontrolador Microchip PIC18F4550 64
Como ejemplo a las ideas presentadas, si una funcin recoge un carcter como
argumento y devuelve un entero, el argumento se pasar a la funcin en el registro
32, y encontraremos el resultado en los registros 16 y 17 una vez completada su eje-
cucin. Sin embargo, si la misma funcin con un carcter como argumento devuelve
un tipo complejo, tal como una estructura, la funcin recoger en los registros 31 y
32 un apuntador a una posicin de memoria, junto con el carcter en el registro 30,
y tras su ejecucin devolver el puntero en los registros 16 y 17.
5.5. Variables globales y estticas
La reserva del espacio para almacenar las variables globales y las estticas, al
contrario que en la gestin de memoria, no se realiza slo en el chero fuente prin-
cipal sino que puede darse en cualquier lugar en el que puedan ser declaradas. Por
tanto, las variables estticas y globales denidas en los cheros secundarios ocupan
un tamao de memoria desconocido al compilar el chero fuente principal. Por este
motivo, siempre que nuestra conversin del GCCtrabaje con variables globales o es-
tticas en archivos que no contengan la funcin main, ser necesario que el usuario
indique al compilador, por linea de comandos, que reserve la cantidad de espacio
necesario para estas variables cuando ejecute la compilacin del archivo principal.
Esta prctica es una mejora sobre los compiladores de microcontroladores, inclui-
do el compilador privativo de este microcontrolador en particular, ya que en casi
todos los compiladores para microcontroladores slo est permitido declarar va-
riables globales en el archivo principal, mientras que GCC permite declararlas en
cualquier archivo de los que compongan el programa.
5.6. Flujo de ejecucin (memoria de cdigo)
La ejecucin de un programa (no trivial) no es totalmente lineal. GCC produci-
r saltos en la ejecucin del cdigo en muchas instrucciones diferentes. Podemos
catalogar los saltos en tres clases: saltos incondicionales, saltos condicionales y lla-
madas a funcin. Tanto los saltos como las llamadas tienen varias peculiaridades
que hemos de tener en cuenta.
Comenzaremos con la memoria de programa de un PIC. Los microcontroladores
PIC18 emplean un contador de programa (PC) de 21 bits, el cual permite direccionar
2M posiciones de memoria de programa. El PC es un registro que almacena la di-
reccin de la prxima instruccin a ejecutar. Este puntero consta de tres registros de
8 bits de los cuales slo se accede directamente a los ocho bits de menor signicado
Compilador de C para el microcontrolador Microchip PIC18F4550 65
a travs del registro conocido como PCL (PC low). Las partes alta y superior de la
direccin pueden accederse mediante los registros PCLATH y PCLATU. Estos regis-
tros se comportan como registros convencionales que permiten lectura y escritura,
sin embargo, los valores que almacenan no pasan al PC hasta que se efecta una es-
critura en el registro PCL. Por tanto, los pasos para cargar un nuevo valor en el PC
son escribir los bits ms signicativos en PCLATU y PCLATH, y una vez hecho esto,
escribir los ocho bits menos signicativos en PCL. La posibilidad de escribir valo-
res arbitrarios en PC ofrece al desarrollador todo lo necesario para poder efectuar
saltos computados (computed gotos en ingls). La tcnica de saltos computados permite,
por ejemplo, hacer uso de valores constantes para inicializar variables de nuestro
programa (no olvidemos que estos microcontroladores se programan escribiendo
nicamente la memoria de programa) y fu uno de los mtodos considerados du-
rante el diseo de este proyecto.
Las instrucciones de salto incondicional modican el registro PC a partir del valor
inmediato especicado en su operando. Las palabras clave de la sentencia anterior
son valor inmediato, porque es un valor especicado en el cdigo de operacin de
la instruccin, y modican. La explicacin a esta ltima reside en que cada instruc-
cin de salto incondicional tiene un comportamiento distinto. La instruccin GOTO
tiene el comportamiento tradicional de salto a cualquier posicin de la memoria y,
debido al tamao de palabra de la memoria de programa y el nmero de posiciones
direccionables, esta instruccin se compone siempre de dos palabras (32 bits). En
cambio, el comportamiento de la otra instruccin de salto incondicional, BRA, es
anlogo a los saltos cortos del modo real en la arquitectura x86: el operando inme-
diato indica un salto relativo al PC, ya sea en sentido positivo o negativo. Debido
al diseo de la arquitectura PIC18, concretamente su manera de direccionar la me-
moria de programa a travs del PC, los saltos resultantes de ejecutar la instruccin
BRA cargan en el PC un valor PC+2+2n (siendo n el valor en complemento a 2 de
los 11 bits menos signicativos de la palabra de instruccin). Por esta limitacin y
por la dicultad de predecir la distancia de los saltos, nuestro back-end nicamente
hace uso de BRApara saltos internos a los bloques de cdigo resultantes de traducir
instrucciones individuales del lenguaje RTL intermedio; las instrucciones RTL que
corresponden saltos en el programa escrito en C siempre resultarn en instrucciones
GOTO, con el consumo de memoria extra que estas instrucciones suponen y que el
desarrollador deber tener en cuenta cuando escriba su cdigo C.
El tipo de salto que efectan las instrucciones de salto condicional, con la pecu-
liaridad de que el PC se modica en caso de satisfacerse una condicin, es similar
al de la instruccin BRA de salto incondicional: el valor que se carga en el PC es,
de nuevo, PC+2+2n. En el caso de las instrucciones de salto condicional, el rango
Compilador de C para el microcontrolador Microchip PIC18F4550 66
de direcciones de memoria al que es posible saltar es mucho ms pequeo que el
admitido por la instruccin BRA, debido a que el tamao del operando inmediato
es de slo 8 bits, frente a los 11 bits del mismo operando para BRA.
La tercera clase de salto, denominada llamadas a funcin, est compuesta por la
instruccin de llamada CALL y tambin por sus complementarias, las instrucciones
de retorno de funcin RETURN, RETLW y RETFIE (esta ltima est orientada a -
nalizar una rutina de atencin a interrupciones, por lo que el cdigo generado por
nuestro compilador no har uso de ella). La instruccin CALL puede verse como
una versin mejorada de GOTO: permite cargar en el PC cualquier posicin abso-
luta de la memoria de programa y, adems, salva en la pila el valor del PC en ese
momento (teniendo en cuenta el post-incremento), por lo que es posible retomar la
ejecucin del bloque de cdigo actual con slo recuperar el valor de la cima de la
pila y almacenarlo de nuevo en el PC. Recuperar el valor anterior del PC es la tarea
que efectan las instrucciones de retorno de funcin. Estos dos grupos de instruc-
ciones facilitan notablemente el uso de subrutinas en el cdigo ensamblador y, por
ende, la implementacin de llamadas a funcin tal como las especica el estndar
del lenguaje C.
Llegado a este punto puede parecer que con las facilidades proporcionadas por
las instrucciones de llamada y retorno, junto a la versatilidad de los saltos condicio-
nales e incondicionales, quedan resueltos todos los problemas relacionados con los
saltos en el ujo de ejecucin de los programas. No es as. Concretamente existe un
problema muy importante ntimamente relacionado con las llamadas a funcin: el
tamao de la pila.
5.7. Pila
Los PIC18 implementan una pila hardware de 31 niveles de profundidad en
la cual se almacenan los valores del PC. Las instrucciones de llamada y retorno de
funciones hacen uso de esta pila. Adems de esto, los PIC18 ofrecen al desarrollador
la posibilidad de leer y escribir a su antojo el valor almacenado en la cima de la
pila (top of stack o TOS), as como modicar el vector que apunta a la cima de la
misma, es decir, modicar qu nivel se considera que es el TOS. No obstante, estas
funcionalidades ms avanzadas no son necesarias para el proyecto que nos atae,
por lo que de aqu en adelante consideraremos que las instrucciones de llamada y
retorno son las nicas que operan sobre la pila de llamadas a funcin.
El tamao jo de la pila supone un lmite pero este problema se agrava con la na-
turaleza cclica de la misma, es decir, que tras efectuar 31 operaciones de apilado, la
nmero 32 escribir en la posicin ms baja de la misma, destruyendo el valor alma-
Compilador de C para el microcontrolador Microchip PIC18F4550 67
cenado en sta y, con toda seguridad, haciendo errnea la ejecucin del programa.
Esta limitacin fsica debe estar siempre presente en la mente del programador que
trabaje con estos microcontroladores, el cual debe evitar las funciones recursivas sin
acotar y reduciendo en la medida de lo posible el nmero de llamadas a funcin en
su programa. Se recomienda no exceder ms all de 28 niveles de anidamiento en
las llamadas a funcin, limitacin muy inferior a la impuesta por el hardware, pero
aconsejada debido a que las llamadas a funciones matemticas, transparentes para
el programador, tambin hacen uso de la pila.
Para la mayora de aplicacones de estos dispositivos, 31 niveles de profundidad
son ms que sucientes, an teniendo en cuenta que las operaciones matemticas
complejas aaden algunas llamadas extra a funcin.
5.8. Biblioteca de funciones matemticas
Los PIC18 incorporan una ALU bsica con soporte para instrucciones lgicas y
aritmticas de suma y resta. Adems, incorpora un multiplicador hardware de 8 bits
que permite implementar funciones complejas de manera ms eciente. Sin embar-
go no incorpora circuitera dedicada especcamente a las operaciones de divisin.
Para efectuar las operaciones de divisin es necesario implementar algoritmos que
hagan uso de las operaciones disponibles. Debido al tamao de estos algoritmos
resulta conveniente no reemplazar cada instruccin de divisin por la implementa-
cin del algoritmo, sino hacer una llamada a una rutina donde se ejecute el mismo
y devolver el resultado mediante los registros o la memoria. De este modo el cdigo
resultante ser ms pequeo a costa de aadir una pequea cantidad de tiempo de
ejecucin.
La solucin elegida para la operacin de multiplicacin es una combinacin de
todas las posibles opciones. Para las operaciones con valores pequeos, la mejor
opcin es hacer uso del multiplicador hardware que incorpora el microcontrolador;
de este modo el cdigo resultante es rpido de ejecutar y muy compacto, por lo que
apenas desperdicia memoria de programa. En cambio, para las multiplicaciones de
valores grandes, los algoritmos que hacen uso del multiplicador son bastante exten-
sos, por lo que a pesar de ser rpidos, desperdician demasiada memoria de progra-
ma. Como alternativa a esto disponemos del algoritmo de Booth, basado en sumas y
restas, que es un algoritmo bastante compacto y no mucho ms lento que el anterior
dependiente del multiplicador hardware. Adems, para la multiplicacin de nme-
ros sin signo tambin existe el sencillo algoritmo de suma+desplazamiento, que es an
ms corto y rpido de ejecutar que el algoritmo de Booth, y es el elegido para efectuar
operaciones de multiplicacin de nmeros enteros sin signo de tamao mediano y
Compilador de C para el microcontrolador Microchip PIC18F4550 68
grande.
Para implementar la operacin de divisin, la opcin elegida, al contrario que
para la multiplicacin, es utilizar un nico algoritmo: el algoritmo de divisin sin res-
tauracin.
En el algoritmo estndar de divisin sin restauracin se resta el divisor al acu-
mulador (previamente inicializado con el dividendo) y en caso de dar un resultado
negativo, se deshace la resta. En la implementacin realizada en nuestro back-end
simplemente no se almacena el resultado de la resta hasta tener la certeza de que
ste es positivo. Para el caso de divisin de nmeros con signo, el algoritmo imple-
mentado obtiene el signo de cada operando antes de efectuar la divisin y, una vez
terminada la operacin de divisin, adeca el resultado en funcin de los signos de
dividendo y divisor.
Pese a ser operaciones costosas y de uso frecuente, la velocidad de ejecucin
obtenida con la biblioteca de funciones matemticas es buena y, lo ms importante,
ahorra mucho espacio en la memoria de programa que, de haberlo implementado
de la manera tradicional (duplicando cdigo) el desperdicio de la misma resultara
excesivo hasta el punto de impedir la realizacin de muchos proyectos con este
microcontrolador, que con nuestra solucin son perfectamente posibles y evita tener
que optar por dispositivos ms avanzados, complejos y caros.
Captulo 6
Implementacin
Al enfrentarnos al problema de portar GCC a una nueva arquitectura objetivo,
el primer paso es modicar convenientemente el conjunto de archivos que denen
el build system. El primer archivo a modicar es cong.sub, situado en la raz del r-
bol de directorios del cdigo fuente. cong.sub contiene los datos de arquitecturas
y sistemas operativos soportados por GCC. La nica modicacin necesaria para
nuestro propsito es aadir una entrada adicional con la nueva arquitectura objeti-
vo pic18. El parche que hace este cambio es el siguiente:
index f9fcdc8..bf1eb36 100755
--- a/config.sub
+++ b/config.sub
@@ -288,7 +288,7 @@ case $basic_machine in
| ns16k | ns32k \
| open8 \
| or32 \
- | pdp10 | pdp11 | pj | pjl \
+ | pdp10 | pdp11 | pic18 | pj | pjl \
| powerpc | powerpc64 | powerpc64le | powerpcle \
| pyramid \
| rx \
Con esto le indicamos al build system de GCC que la arquitectura pic18 existe y
es vlida. Para conseguir que GCC la soporte, debemos modicar otro chero ms:
el de conguracin del build system. En realidad, la modicacin la efectuaremos
sobre el archivo de entrada del programa autoconf. autoconf forma parte de las he-
rramientas de GNU denominadas autotools, y su cometido es generar un programa
de conguracin automtica que ayude en la automatizacin del proceso de com-
pilacin del software.
69
Compilador de C para el microcontrolador Microchip PIC18F4550 70
A continuacin se muestra el parche para congure.ac:
diff --git a/configure.ac b/configure.ac
index 337e11d..f11ac53 100644
--- a/configure.ac
+++ b/configure.ac
@@ -498,11 +498,62 @@ case "${target}" in
# No hosted I/O support.
noconfigdirs="$noconfigdirs target-libssp"
;;
+ pic18-
*
-
*
)
+ noconfigdirs="$noconfigdirs target-libssp"
+ ;;
powerpc-
*
-aix
*
| rs6000-
*
-aix
*
)
noconfigdirs="$noconfigdirs target-libssp"
;;
esac
+# Disable target libiberty for some systems.
+case "${target}" in
+
*
-
*
-kaos
*
)
+ # Remove unsupported stuff on all kaOS configurations.
+ skipdirs="target-libiberty"
+ ;;
+
*
-
*
-netbsd
*
)
+ # Skip some stuff on all NetBSD configurations.
+ noconfigdirs="$noconfigdirs target-libiberty"
+ ;;
+
*
-
*
-netware
*
)
+ noconfigdirs="$noconfigdirs target-libiberty"
+ ;;
+
*
-
*
-rtems
*
)
+ skipdirs="${skipdirs} target-libiberty"
+ ;;
+
*
-
*
-tpf
*
)
+ noconfigdirs="$noconfigdirs target-libiberty"
+ ;;
+
*
-
*
-vxworks
*
)
+ noconfigdirs="$noconfigdirs target-libiberty"
Compilador de C para el microcontrolador Microchip PIC18F4550 71
+ ;;
+ sh
*
-
*
-pe|mips
*
-
*
-pe|
*
arm-wince-pe)
+ noconfigdirs="$noconfigdirs target-libiberty"
+ ;;
+ arm
*
-
*
-symbianelf
*
|arm
*
-
*
-linux-androideabi)
+ noconfigdirs="$noconfigdirs target-libiberty"
+ ;;
+ avr-
*
-
*
)
+ noconfigdirs="$noconfigdirs target-libiberty"
+ ;;
+ m68hc11-
*
-
*
|m6811-
*
-
*
|m68hc12-
*
-
*
|m6812-
*
-
*
)
+ noconfigdirs="$noconfigdirs target-libiberty"
+ ;;
+ pic18-
*
-
*
)
+ noconfigdirs="$noconfigdirs target-libiberty"
+ ;;
+ picochip-
*
-
*
)
+ noconfigdirs="$noconfigdirs target-libiberty"
+ ;;
+ mips
*
-sde-elf
*
)
+ skipdirs="$skipdirs target-libiberty"
+ ;;
+ ip2k-
*
-
*
)
+ noconfigdirs="$noconfigdirs target-libiberty"
+ ;;
+esac
+
# Disable libstdc++-v3 for some systems.
case "${target}" in
*
-
*
-vxworks
*
)
@@ -516,6 +567,8 @@ case "${target}" in
avr-
*
-
*
)
noconfigdirs="$noconfigdirs target-libstdc++-v3"
;;
+ pic18-
*
-
*
)
+ noconfigdirs="$noconfigdirs target-libstdc++-v3"
esac
Compilador de C para el microcontrolador Microchip PIC18F4550 72
# Disable Fortran for some systems.
@@ -524,6 +577,9 @@ case "${target}" in
# See <http://gcc.gnu.org/ml/gcc-patches/2004-11/msg005\
72.html>.
unsupported_languages="$unsupported_languages fortran"
;;
+ pic18-
*
-
*
)
+ unsupported_languages="$unsupported_languages fortran"
+ ;;
esac
# Disable Java if libffi is not supported.
@@ -657,6 +713,9 @@ case "${target}" in
mmix-
*
-
*
)
noconfigdirs="$noconfigdirs target-libffi target-boehm-\
gc"
;;
+ pic18-
*
-
*
)
+ noconfigdirs="$noconfigdirs ${libgcj} target-libffi"
+ ;;
powerpc-
*
-aix
*
)
# copied from rs6000-
*
-
*
entry
noconfigdirs="$noconfigdirs ${libgcj}"
@@ -955,6 +1014,13 @@ case "${target}" in
mt-
*
-
*
)
noconfigdirs="$noconfigdirs sim"
;;
+ pic18-
*
-
*
)
+ noconfigdirs="$noconfigdirs target-libquadmath"
+ unsupported_languages="$unsupported_languages ada c++ j\
ava objc obj-c++"
+ ;;
+ picochip-
*
-
*
)
+ noconfigdirs="$noconfigdirs target-libiberty"
+ ;;
powerpc-
*
-aix
*
)
# copied from rs6000-
*
-
*
entry
noconfigdirs="$noconfigdirs gprof"
Compilador de C para el microcontrolador Microchip PIC18F4550 73
@@ -2264,6 +2330,17 @@ case "${target}" in
mips
*
-
*
-
*
linux
*
| mips
*
-
*
-gnu
*
)
target_makefile_frag="config/mt-mips-gnu"
;;
+ pic18-
*
-
*
)
+ NCN_STRICT_CHECK_TOOLS(AS_FOR_TARGET, gpasm)
+ AC_PATH_PROG(AS_FOR_TARGET, gpasm)
+ NCN_STRICT_CHECK_TOOLS(LD_FOR_TARGET, gplink)
+ AC_PATH_PROG(LD_FOR_TARGET, gplink)
+ NCN_STRICT_CHECK_TOOLS(AR_FOR_TARGET, gplib)
+ extra_arflags_for_target=" -c"
+ NCN_STRICT_CHECK_TOOLS(NM_FOR_TARGET, gplib)
+ NCN_STRICT_CHECK_TOOLS(RANLIB_FOR_TARGET, true)
+ NCN_STRICT_CHECK_TOOLS(STRIP_FOR_TARGET, gpstrip)
+ ;;
*
-
*
-linux
*
|
*
-
*
-gnu
*
|
*
-
*
-k
*
bsd
*
-gnu |
*
-
*
-kopensolaris\
*
-gnu)
target_makefile_frag="config/mt-gnu"
;;
Despus de esta modicacin es necesario ejecutar autoconf para que genere el
nuevo programa de conguracin del build system.
Hecho esto, el sistema de compilacin de GCCya conoce la existencia de la arqui-
tectura objetivo pic18 y puede compilar GCC con el back-end para esta arquitectura.
Ahora queda denirla.
Las deniciones de arquitectura residen en el subdirectorio gcc/cong. En este di-
rectorio crearemos un subdirectorio con el mismo nombre que nuestra arquitectura
destino, pic18, donde albergaremos los archivos de especicacin y descripcin de
arquitectura.
Los archivos principales que describen la arquitectura y que han sido el centro
de trabajo fundamental durante el transcurso de este proyecto son:
pic18.md
pic18.h
pic18.c
El primero, pic18.md, contiene la descripcin de la mquina. En l especicamos
a GCC cmo tiene que traducir las instrucciones RTL generadas por el middle-end a
cdigo ensamblador del PIC18.
Compilador de C para el microcontrolador Microchip PIC18F4550 74
Recordemos el proceso de traduccin que efecta el compilador. Primero, el
front-end lee el archivo de cdigo fuente y crea, a partir del mismo, una estructura
de rbol sintctico. En segundo lugar, genera una lista de instrucciones en el len-
guaje intermedio RTL llamadas insn. Por ltimo, empajera las insn de esta lista, una
por una, con los patrones de instruccin denidos y genera los bloques de cdigo
ensamblador correspondientes a cada insn.
El archivo de descripcin de mquina (Machine Description), pic18, es el que con-
tiene los patrones de instruccin que protagonizan el tercer paso de la compilacin.
Segn los patrones denidos en l, el middle-end generar unas insn u otras, con
el objetivo de que todas las instrucciones RTL que compongan el programa justo
antes de la fase de emparejamiento tengan asociado un patrn de instrucciones en
ensamblador.
El archivo pic18.h contiene la especicacin de la mquina. Est compuesto por
macros que indican a GCC cmo es la arquitectura objetivo. Entre otros datos, in-
cluye informacin sobre el nmero de registros, el nombre de cada uno, para qu
puede usarse y para qu no, el tamao de palabra, tamao de cada tipo de dato,
la alineacin en memoria y los nombres de las distintas secciones dentro del cdi-
go ensamblador. Para algunas de estas macros no es posible utilizar slamente el
preprocesador, por lo que, en su lugar, hemos optado por emplear funciones en C
externas, que hagan ms sencillo el desarrollo y faciliten la comprensin y depura-
cin del cdigo. Debido a la exibilidad que otorga el uso de funciones ejecutadas
en tiempo de ejecucin de nuestro compilador frente al uso de macros, los desarrolla-
dores de GCC han optado por ir migrando la mayora de macros a target hooks.
El tercer archivo de la lista, pic18.c, contiene las funciones auxiliares menciona-
das en el prrafo anterior, funciones auxiliares para la generacin de cdigo ensam-
blador y los target hooks que especican la mquina objetivo. Los target hooks son
datos que indican a GCC cmo es la arquitectura objetivo y apuntadores a funcio-
nes que deciden dinmicamente las caractersticas de la mquina objetivo, durante
la ejecucin del compilador (al contrario de lo que ocurre con las macros, que se
evalan una nica vez al compilar GCC y apenas otorgan facilidades para utilizar
cdigo condicional).
A parte de estos archivos, existen otros que sirven de apoyo a la descripcin y
especicacin de la arquitectura.
El archivo libgcc.S es el archivo principal de la biblioteca auxiliar libgcc. Esta
biblioteca contiene todas las operaciones matemticas que no estn soportadas di-
rectamente por la arquitectura pero s por el estndar ANSI C.
Compilador de C para el microcontrolador Microchip PIC18F4550 75
6.1. Creacin de patrones
Como primer paso para explicar la descripcin tenemos que comprender como
funcionan los patrones. Para ello examinaremos los dos tipos de patrones existen-
tes. Por un lado tenemos los patrones denidos mediante dene_insn y por otro los
denidos con dene_expand. Ambos estn constituidos de forma similar, pero los
segundos sirven para generar otros insn o agrupar una familia de los mismos. Los
primeros se comportan, grosso modo, como generadores de cdigo y son los que
realmente producen el texto del cdigo ensamblador nal.
6.1.1. dene_insn
El lenguaje de denicin de patrones es bastante similar al lenguaje funcional
Lisp: cada denicin es una lista con un nmero variable de elementos, los cua-
les pueden ser elementos nales u otras listas. Las sentencias dene_insn toman los
siguientes argumentos:
1. Nombre. Para GCC existen varias instrucciones insn que realizan tareas bsicas
y que el middle-end utiliza durante la fase de generacin de RTL. Estas son -
jas, dado que GCC tiene la posibilidad de adecuarse a los patrones denidos;
y, de los que tenga, elegir la mejor combinacin para realizar cada opera-
cin. En cualquier caso, debemos denir sucientes insn para que GCC pueda
componer el total de operaciones necesarias. Para esto podemos usar uno de
los nombres conocidos e implementar la accin requerida o emitir, con de-
ne_expand las insn que se necesiten para conseguir el objetivo de otra manera.
Si usamos un nombre no conocido por GCC, o simplemente no le damos nom-
bre (usando la cadena vaca), el compilador no utilizar este patrn durante
la fase de generacin de RTL, pero con ellos permitimos que varias insn sim-
ples puedan ser combinadas posteriormente. A los patrones no estndar o sin
nombre se les suele dar un nombre basado en su nalidad y comenzando con
asterisco, para diferenciarlos de los patrones estndar.
2. Plantilla RTL. Vector de expresiones RTL incompletas que indican a GCC cmo
debe usar el patrn. Con expresiones RTL incompletas nos referimos a que son
plantillas de instruccin con elementos desconocidos o huecos, que GCC com-
pletar, de acuerdo a ciertas reglas, para crear el texto del cdigo ensamblador
que nalmente aparezca en el cdigo ensamblador de salida.
3. Condicin. Es una expresin en C que permite comprobar si el cuerpo de este
patrn de instruccin es el adecuado para la insn.
Compilador de C para el microcontrolador Microchip PIC18F4550 76
4. Plantilla de salida. Cadena de caracteres que indica a GCC qu cdigo ensam-
blador debe emitir para el insn actual. Es posible utilizar una cadena de carac-
teres, un vector compuesto por varias cadenas o un bloque de cdigo C que
devolver el cdigo ensamblador correspondiente.
5. Atributos. Vector de valores para los atributos de los insn que encajen con este
patrn.
6.1.2. Ejemplo de dene_insn
Ilustramos lo comentado con el siguiente ejemplo:
(define_insn "xorhi3"
[(set (match_operand:HI 0 "register_operand" "=v,v")
(xor:HI (match_operand:HI 1 "register_operand" "%0,0
")
(match_operand:HI 2 "reg_or_int_operand" "i,
v"))))]
""
"@
movlw low %2\n\txorwf %0,F\n\tmovlw high %2\n\txorwf
%0+1,F
movf %2,W\n\txorwf %0,F\n\tmovf %2+1,W\n\txorwf %0+1,F"
)
El patrn xorhi3 es un patrn estndar. Realiza la disyuncin exclusiva entre el
operando 1 y el operando 2, guardando el resultado en el operando 0. La plantilla
RTL muestra esta accin, la primera lista encabezada con set, tiene dos operandos: el
primero es el registro que ser modicado, y el segundo una lista xor que contiene
a su vez dos registros.
La forma de especicar el tipo de un operando y las restricciones que debe cum-
plir la vemos con este ejemplo:
(match_operand:HI 0 "register_operand" "=v,v")
Usamos match_operand para que unique con un operando. HI especica el mo-
do. El modo de un operando indica de qu tipo y de qu tamao son los datos que
permite este operando. El nmero de operando, 0 para el ejemplo, debe ser parte de
una lista de nmeros consecutivos que comience por cero. El siguiente parmetro
de match_operand es el predicado, que determina si existe la relacin deseada sobre
el operando, es decir, si el operando es vlido o no. En este ejemplo, el predicado
Compilador de C para el microcontrolador Microchip PIC18F4550 77
es register_operand y especica que este operando debe ser un registro
1
. A continua-
cin vemos =v,v. Esta cadena especica una restriccin de clase al operando. Esta
restriccin no se examina en la fase de emparejamiento de patrones, sino en la fase
de recarga. En el ejemplo, register_operand exige que el operando sea un registro,
pero con la restriccin v exigimos que en la recarga el resultado sea uno de los re-
gistros de la clase registros virtuales de nuestra mquina. En un principio GCC usar
innitos registros para emparejar patrones, pero en la recarga asignar los registros
virtuales disponibles y, en caso de no quedar ninguno libre, buscar liberar alguno
para poder usarlo.
En la restriccin vemos tambin un signo =, que indica que el operando es de
salida, es decir, que su valor ser sobreescrito al terminar la instruccin. Vemos
como existen dos v indicando que la instruccin en realidad corresponde a un vector
de dos instrucciones, ambas con la misma forma, pero diferentes restricciones en
sus operandos. Si se usan dos restricciones en un operando, todos los operandos
con restricciones deben tener un vector con el mismo nmero de restricciones, en
este caso dos.
Para el segundo registro (el primero de la operacin xor) las restricciones mues-
tran el valor 0. El carcter 0 como restriccin indica que el registro a elegir para este
operando en la fase de recarga ser el mismo que el numerado como 0, es decir,
para el caso que estamos tratando, el resultado y un operando comparten registro.
Otro valor que llama la atencin en las restricciones es el % de las restricciones
del segundo operando (numerado como 1). El % indica a GCC que los operandos
son intercambiables, es decir, que la operacin A = xor B C es equivalente a A = xor
C B. Dejamos con esto libertad a GCC para escoger registro destino entre los dos
que se establecern como entrada, decisin que tomar en base al registro que no se
vaya a usar tras la operacin, dado que el elegido ser sobreescrito con el resultado.
En el tercer operando nos encontramos con un nuevo predicado y una nueva
restriccin. Como podemos ver, la restriccin i se reere a valores constantes ente-
ros, y el predicado reg_or_int_operand slo permitir registros o valores constantes
enteros.
Vista la forma general en que se dene una insn, nos queda ver la plantilla de
salida. Lo vemos partiendo de este ejemplo:
"@
movlw low %2\n\txorwf %0, F\n\tmovlw high %2\n\txorwf
%0+1, F
1
Lo que GCC considera un registro fsico.
Compilador de C para el microcontrolador Microchip PIC18F4550 78
movf %2, W\n\txorwf %0, F\n\tmovf %2+1, W\n\txorwf %0+1,
F"
Hay dos formas de especicar la plantilla de salida: como una cadena, que con-
tiene los argumentos especicados con%n, donde n es un nmero de operando, y
como un trozo de cdigo C que devuelve una cadena de caracteres con el cdigo
ensamblador. En este ejemplo vemos el primer caso. Como comentamos, al usar
dos restricciones en los operandos, en realidad estamos deniendo dos patrones de
instruccin diferentes (con forma similar). Por ello es por lo que debemos usar @ al
inicio de la cadena. Posteriormente cada linea escrita corresponder a un juego de
restricciones en el orden en que se especican. La primera lnea corresponde a la
primera forma, con los operandos v, 0, y i, que hace la operacin xor con dos ope-
randos de entrada, un valor entero constante y un registro que es el mismo donde
queda establecida la salida. La segunda lnea corresponde a las restricciones v, 0 y
v, donde se opera sobre dos registros y uno de ellos ser el escogido para el resul-
tado. Como vemos, el cdigo ensamblador realiza tales operaciones pero deja los
parmetros de entrada y salida sin especicar, hasta que, en la fase de recarga, se
designan los valores denitivos para cada operando, sustituyendo los valores %n
de la plantilla por el texto que identica a los registros designados.
En el caso de que queramos hacer este mismo cdigo con un trozo de cdigo C,
se escribira de esta forma:
{
if (which_alternative==0)
{
output_asm_insn("movlw low %2", operands);
output_asm_insn("xorwf %0, F", operands);
output_asm_insn("movlw high %2", operands);
}
else
{
output_asm_insn("movf %2, W", operands);
output_asm_insn("xorwf %0, F", operands);
output_asm_insn("movf %2+1, W", operands);
}
output_asm_insn("xorwf %0+1, F", operands);
return "";
}
Compilador de C para el microcontrolador Microchip PIC18F4550 79
Evaluando el valor which_alternative sabemos qu alternativa en las restricciones
se ha tomado. Con output_asm_insn damos salida a la cadena especicada en el
primer operando con los valores del vector de rtx denidos en el segundo operando.
Por defecto, podemos utilizar operands, que es el vector que contiene los operandos
del insn actual, pero podemos crear o modicar nuevos vectores para operaciones
ms complejas. El trozo de cdigo C debe terminar devolviendo una cadena de
caracteres. Es posible devolver todo el cdigo ensamblador en esta cadena o, como
hacemos en este ejemplo, generarlo paso por paso y terminar el bloque de cdigo C
devolviendo una cadena vaca.
En este ejemplo, usar cdigo C no aporta ninguna diferencia, pero podemos mo-
dicar variables globales, crear etiquetas, efectuar ejecucin condicional, llamar a
procedimientos, incluir datos de depuracin o emitir nuevas instrucciones segn
lo deseemos en insn ms complejas. Gracias a esto tenemos total exibilidad para
generar cdigo tras el emparejamiento.
El cdigo, a diferencia de la cadena, est encerrado entre llaves. Las cadenas
cuyo primer carcter es una arroba @ indican salida mltiple. En caso contrario,
solo hay un bloque de cdigo de salida que puede ocupar varias lineas.
Hay ms opciones para especicar la plantilla de salida, pero en la denicin de
la mquina hemos usado tan solo la opcin de bloques de cdigo C para generar
cdigo ensamblador.
6.1.3. Dene_expand
Para terminar con la compresin de los tipos de patrones, tenemos que hablar
del segundo tipo de patrn, los denidos con dene_expand. El objetivo de estos pa-
trones es crear nuevos insn en la fase de generacin de RTL. Sirven para expandir o
renar un patrn estndar, no para emitir cdigo ensamblador, y su sintaxis consta
de cuatro parmetros:
1. Nombre. Dado que slo se utilizan durante la fase de generacin de RTL, deben
tener un nombre estndar.
2. Plantilla RTL. Vector de expresiones RTL incompletas. Su forma es idntica a
la plantilla RTL de dene_insn.
3. Condicin. Al igual que la plantilla RTL, es idntica a la condicin en una de-
claracin dene_insn.
4. Sentencia de preparacin. Bloque de cdigo C que es ejecutado antes de generar
el cdigo RTL. El propsito de estas declaraciones es preparar registros tem-
Compilador de C para el microcontrolador Microchip PIC18F4550 80
porales para su uso como operandos internos o emitir otros insn de manera
manual mediante las funciones de generacin de insn.
En el proyecto actual hemos usado los patrones de expansin con dos propsi-
tos distintos. En los patrones de asignacin los hemos aplicado con la intencin de
dividir dichos patrones en otros ms sencillos que puedan tratarse de forma ms
simple con dene_insn. El otro uso dado a los patrones de expansin ha sido con las
operaciones de multiplicacin y divisin, y la intencin en estos casos no era sub-
dividir los casos sino adaptar los argumentos de entrada a los requisitos estrictos
impuestos por las funciones de la biblioteca de operaciones matemticas.
6.2. Asignacin - los patrones mnimos.
GCC necesita conocer la denicin de un conjunto mnimo de patrones a partir
de los cuales pueda componer el resto de patrones de instruccin. De forma obli-
gatoria, necesitamos describir las operaciones de asignacin o movimiento de re-
gistros con todos los tipos que queramos considerar en la arquitectura. Como tipos
permitidos existen los QI (un byte), HI (dos), SI (cuatro bytes) y SF (cuatro bytes de
un valor en coma otante). Debemos denir patrones de insn para todos los movi-
mientos posibles entre todas las posibilidades de registros, constantes y memoria,
es decir, desde un registro, memoria o una constante, hacia un registro o a una po-
sicin de memoria.
Veremos las operaciones para el tipo HI, pero el formato es extrapolable a los de-
ms tipos. Tenemos un dene_expand que es necesario para que GCC tenga el patrn
estndar. En l tratamos cuatro casos que suponen movimientos entre posiciones de
memoria, gracias a lo cual evitamos sumar otros cuatro casos a los ocho existentes.
El patrn estndar movhi unicar con:
(define_expand "movhi"
[(set (match_operand:HI 0 "target_mov_operand")
(match_operand:HI 1 "source_mov_operand"))]
""
{
if ((GET_CODE(operands[0]) == MEM) &&
(GET_CODE(operands[1])==MEM))
{
rtx tmp = gen_rtx_REG(HImode, PIC_RETURN_REGISTER);
emit_insn( gen_movhi(tmp, operands[1]) );
Compilador de C para el microcontrolador Microchip PIC18F4550 81
emit_insn( gen_movhi(operands[0], tmp) );
DONE;
}
}
)
Con esto GCC ver el patrn estndar que necesita para generar el RTL, y tam-
bin ver que no tiene informacin para generar cdigo ensamblador (se trata de
de un patrn de expansin, no de instruccin).
Para el caso de ser una operacin cuyos parmetros sean ambos de acceso a
memoria, GCC emite dos insn correspondientes a movimientos del mismo tipo de
dato. El primer insn corresponde a un movimiento desde la posicin de memoria
origen hacia el registro dado por la funcin gen_rtx_REG, que devuelve, en este
caso, el registro usado para retorno de las funciones y que podemos usar siempre
sin tener que salvar su valor. El segundo, copiar el contenido de ese registro en
la posicin de memoria destino. Este bloque de cdigo naliza con la sentencia
DONE, que indica a GCC que la expansin ha nalizado con xito y puede dar
por nalizado el tratamiento de este insn en la fase de generacin de cdigo RTL.
Cuando no se trate de este caso, el RTL generado corresponder al de la plantilla
denida en la denicin de expansin (segundo parmetro del dene_expand).
Llegada la fase de generacin de cdigo ensamblador, GCC buscar el empare-
jamiento de patrones comparando cada RTX (expresin en lenguaje RTL) con los
patrones denidos. Para este proceso no tiene en cuenta el nombre (primer par-
metro) de cada denicin, slo compara, uno a uno y por orden de aparicin en el
archivo de descripcin de mquina, los patrones de cada denicin de instruccin
(los denidos con dene_insn). Para el ejemplo que estamos tratando, la unicacin
tendr xito al llegar a esta denicin:
(define_insn "
*
movhi"
[(set (match_operand:HI 0 "target_mov_operand" "=v,v,S")
(match_operand:HI 1 "source_mov_operand" "U,S,U"))]
""
{
return pic_movhi(insn, operands, NULL);
}
)
Esta denicin resulta muy clara gracias al uso de una funcin auxiliar, que es la
responsable de analizar de qu tipo de movimiento de datos se trata y producir el
cdigo ensamblador correspondiente. A parte de esto, la mayor curiosidad reside
Compilador de C para el microcontrolador Microchip PIC18F4550 82
en el asterisco que precede al nombre: con esto logramos tener un nombre descrip-
tivo para la persona que estudie el RTL generado durante las distintas etapas de
compilacin y, al mismo tiempo, no confundir al compilador con un posible patrn
de instruccin extra a tener en cuenta durante la etapa de generacin de RTL.
Para ayudar a comprender la etapa de generacin de cdigo ensamblador, he-
mos optado por incluir el cdigo de la funcin pic_movhi en este documento.
const char
*
pic_movhi (rtx insn, rtx operands[], int
*
l
ATTRIBUTE_UNUSED)
{
rtx op_source = ignore_CONST_SUBREG(operands[1]);
rtx op_target = ignore_CONST_SUBREG(operands[0]);
enum rtx_code code_source = GET_CODE(op_source);
enum rtx_code code_target = GET_CODE(op_target);
rtx xoperands[3];
/
*
Check that it is not a MEM-to-MEM transfer.
If so, an error in movhi expansion happened.
*
/
/
*
There are 3 cases:
*
/
/
*
A) - R = {I, R, SR, SR+I}
*
/
/
*
B) - R = M(x)
*
/
/
*
C) - M(x) = {I, R, SR, SR+I}
*
/
if (code_target == REG)
{
if (code_source != MEM)
{
/
*
Case A : R = {I, R, SR, SR+I}
*
/
switch (code_source)
{
case CONST_INT:
if ((INTVAL(op_source) & 0xFF) == 0)
{
ASM_OUT("clrf %0");
}
else
Compilador de C para el microcontrolador Microchip PIC18F4550 83
{
ASM_OUT("movlw low %1");
ASM_OUT("movwf %0");
}
if ((INTVAL(op_source) & 0xFF00) == 0)
return "clrf %0+1";
else
return "movlw high %1\n"
"\tmovwf %0+1";
break;
case REG:
/
*
If there is register overlapping between
source and target,
make the copying so target will end with
the expected value.
Final value in source is not our business
at this point.
*
/
if (true_regnum(op_target) >
true_regnum(op_source))
{
return "movff %1+1, %0+1\n"
"\tmovff %1, %0";
}
else
{
return "movff %1, %0\n"
"\tmovff %1+1, %0+1";
}
break;
case SYMBOL_REF:
return "movlw low %1\n"
"\tmovwf %0\n"
"\tmovlw high %1\n"
"\tmovwf %0+1";
break;
Compilador de C para el microcontrolador Microchip PIC18F4550 84
case PLUS:
xoperands[0] = op_target;
xoperands[1] = ignore_CONST_SUBREG(
XEXP(op_source, 0));
xoperands[2] = ignore_CONST_SUBREG(
XEXP(op_source, 1));
if ((GET_CODE(xoperands[1]) == SYMBOL_REF) &&
(GET_CODE(xoperands[2]) == CONST_INT))
{
output_asm_insn(
"movlw (low %1) + (low %2)\n"
"\tmovwf %0\n"
"\tmovlw (high %1) + (high %2)\n"
"\tmovwf %0+1", xoperands);
return "";
}
/
*
else ERROR
*
/
break;
default: /
*
ERROR
*
/
break;
}
}
else /
*
code_source == MEM
*
/
{
/
*
Case B : R = M(x)
*
/
/
*
Write address to FSR...
*
/
pic_direct_FSR(ignore_CONST_SUBREG(
XEXP(op_source, 0)));
/
*
... and copy from INDF to registers.
*
/
if (last_fsr != 0)
last_fsr++;
ASM_OUT("movff INDF0, %0");
ASM_OUT("infsnz FSR0L, F");
Compilador de C para el microcontrolador Microchip PIC18F4550 85
ASM_OUT("incf FSR0H, F");
ASM_OUT("movff INDF0, %0+1");
return "";
}
}
else /
*
code_target != REG
*
/
{
/
*
Case C : M(x) = {I, R, SR, SR+I}
*
/
/
*
Write address to FSR...
*
/
pic_direct_FSR(ignore_CONST_SUBREG(
XEXP(op_target, 0)));
/
*
... and copy from source to INDF.
*
/
switch (code_source)
{
case CONST_INT:
if ((INTVAL(op_source) & 0xFF) == 0)
{
ASM_OUT("clrf INDF0");
}
else
{
ASM_OUT("movlw low %1");
ASM_OUT("movwf INDF0");
}
if (last_fsr != 0)
last_fsr++;
ASM_OUT("infsnz FSR0L, F");
ASM_OUT("incf FSR0H, F");
if ((INTVAL(op_source) & 0xFF00) == 0)
return "clrf INDF0";
else
return "movlw high %1\n"
"\tmovwf INDF0";
break;
Compilador de C para el microcontrolador Microchip PIC18F4550 86
case REG:
if (last_fsr != 0)
last_fsr++;
ASM_OUT("movff %1, INDF0");
ASM_OUT("infsnz FSR0L, F");
ASM_OUT("incf FSR0H, F");
ASM_OUT("movff %1+1, INDF0");
return "";
break;
case SYMBOL_REF:
if (last_fsr != 0)
last_fsr++;
ASM_OUT("movlw low %1");
ASM_OUT("movwf INDF0");
ASM_OUT("infsnz FSR0L, F");
ASM_OUT("incf FSR0H, F");
ASM_OUT("movlw high %1");
ASM_OUT("movwf INDF0");
return "";
break;
case PLUS:
xoperands[0] = op_target;
xoperands[1] = ignore_CONST_SUBREG(
XEXP(op_source,0));
xoperands[2] = ignore_CONST_SUBREG(
XEXP(op_source,1));
if ((GET_CODE(xoperands[1]) == SYMBOL_REF) &&
(GET_CODE(xoperands[2]) == CONST_INT))
{
if (last_fsr != 0)
Compilador de C para el microcontrolador Microchip PIC18F4550 87
last_fsr++;
output_asm_insn("movlw (low %1) + (low %2)",
xoperands);
output_asm_insn("movwf INDF0", xoperands);
output_asm_insn("infsnz FSR0L, F", xoperands);
output_asm_insn("incf FSR0H, F", xoperands);
output_asm_insn("movlw (high %1) + (high %2)",
xoperands);
output_asm_insn("movwf INDF0", xoperands);
return "";
}
/
*
else ERROR
*
/
break;
default: /
*
ERROR
*
/
break;
}
}
fprintf(stderr, "RTX not known by movhi.\n");
debug_rtx(insn);
return "UNKNOWN_MOVHI";
}
Vista esta operacin, hemos alcanzado el formato de instruccin ms complejo
del chero de descripcin. Podemos observar operaciones ms largas en el chero
C de apoyo, pero no ms complejas que sta. Por lo tanto, vemos cmo desaparece
la complicacin de la descripcin de una mquina mediante la especicacin de
operaciones sencillas.
Los otros tipos de movimiento con QI, SI y SF son anlogos a los analizados para
el tipo HI, en formato y en idea de funcionamiento. Podemos verlos en el listado del
archivo de la descripcin de mquina, pero no tienen distincin en su operativa a
la que hemos presentado en esta seccin.
Compilador de C para el microcontrolador Microchip PIC18F4550 88
Nombre Descripcin
absM2 Valor absoluto.
addM3 Suma.
andM3 Conjuncin lgica.
ashlM3 Desplazamiento aritmtico a la izquierda.
ashrM3 Desplazamiento aritmtico a la derecha.
cmpM Comparacin de valores.
divmodMN4 Divisin y mdulo.
udivmodMN4 Divisin y mdulo sin signo.
extendMN2 Extensin de signo de un dato de tipo M a otro de tipo N
(QI=>HI, QI=>SI, HI=>SI).
zero_extendMN2 Extensin de ceros de un dato de tipo M a otro de tipo N
(QI=>HI, QI=>SI, HI=>SI).
iorM3 Disyuncin lgica.
lshrM3 Desplazamiento lgico a la derecha.
mulM3 Multiplicacin.
mulhisi3 Multiplicacin de valores HI con resultado en tipo SI.
umulhisi3 Multiplicacin de valores HI con resultado en tipo SI. Sin
signo.
mulqihi3 Multiplicacin de valores QI con resultado en tipo HI.
umulqihi3 Multiplicacin de valores QI con resultado en tipo HI. Sin
signo.
negM2 Negacin.
one_cmplM2 Complemento a uno.
rotlM3 Rotacin izquierda.
rotrM3 Rotacin derecha.
subM3 Resta.
xorM3 Disyuncin exclusiva.
Cuadro 6.1: Resumen de patrones denidos I: Aritmtico-lgicas
6.3. Los patrones del back-end
Recordemos que GCC buscar, entre los patrones disponibles, aquellos patro-
nes que le permitan efectuar la operacin deseada. Por ejemplo, podemos denir el
complemento a uno y el and, y GCC los usar para denir el or. Pero cuantos ms
patrones describamos, mejor funcionamiento tendr GCC, al usar patrones direc-
tos y no mezclas de ellos, con el consiguiente almacenamiento temporal que esto
conlleva.
En las tablas 6.1 y 6.2 podemos ver los patrones denidos.
Mostraremos algunos de diferente dicultad para tener ms aanzada la idea
general. Comenzando por lo fcil, tenemos el patrn zero_extendqihi2.
(define_insn "zero_extendqihi2"
Compilador de C para el microcontrolador Microchip PIC18F4550 89
Nombre Descripcin
beq Salto condicional si igual.
bge Salto condicional si mayor o igual.
bgeu Salto condicional si mayor o igual. Versin sin signo.
bgt Salto condicional si mayor.
bgtu Salto condicional si mayor. Versin sin signo.
ble Salto condicional si menor o igual.
bleu Salto condicional si menor o igual. Versin sin signo.
blt Salto condicional si menor.
bltu Salto condicional si menor. Versin sin signo.
bne Salto condicional si no igual.
call Llamada a subrutina.
call_value Llamada a subrutina con parmetro.
cbranchM4 Salto condicional.
jump Salto incondicional.
indirect_jump Salto incondicional indirecto.
movsf Movimiento en punto otante.
movM_from_mem Movimiento de memoria a registro en tipo M(QI,HI,SI,SF).
movM_mem_to_mem Movimiento de una posicin de memoria a otra, de un da-
to de tipo M (QI,HI,SI,SF).
movM_reg Movimiento de registro y constante a registro en tipo M
(QI, HI, SI, SF).
movM_to_mem Movimiento de registro y constante a memoria en tipo M
(QI, HI, SI, SF).
nop No operacin.
pushM Introduccin de dato en la pila.
popM Recuperacin de dato de la pila.
return Vuelta de subrutina.
Cuadro 6.2: Resumen de patrones denidos II: Control
Compilador de C para el microcontrolador Microchip PIC18F4550 90
[(set (match_operand:HI 0 "register_operand" "=v")
(zero_extend:HI (match_operand:QI 1 "
register_operand" "0")))]
""
"clrf %0+1"
)
Ante la plantilla de entrada, que admite un valor del tipo QI obtenemos un dato
de tipo HI, dos bytes, que es la extensin sin signo del valor de entrada. La for-
ma normal de hacer esto es simplemente rellenar con ceros la parte de valor nal
todava no denida.
Aumentando un poco el grado de complejidad de una instruccin, veamos el
patrn neghi2.
(define_insn "neghi2"
[(set (match_operand:HI 0 "register_operand" "=v,v,v")
(neg:HI (match_operand:HI 1 "reg_or_int_operand" "i
,0,v")))]
""
{
switch (which_alternative)
{
case 0:
ASM_OUT("movlw low %1");
ASM_OUT("movwf %0");
ASM_OUT("movlw high %1");
ASM_OUT("movwf %0+1");
break;
case 1:
break;
case 2:
ASM_OUT("movff %1, %0");
ASM_OUT("movff %1+1, %0+1");
break;
default:
break;
Compilador de C para el microcontrolador Microchip PIC18F4550 91
}
ASM_OUT("comf %0+1, F");
ASM_OUT("negf %0");
ASM_OUT("btfsc STATUS, C");
ASM_OUT("incf %0+1, F");
return "";
}
)
Este patrn tiene tres restricciones y con ello tres posibles alternativas. Podra-
mos haber usado una cadena comenzando por arroba y una lnea por cada posibili-
dad, pero la opcin de utilizar un bloque de cdigo C resulta ms legible debido a la
extensin de los cdigos en ensamblador restultantes. Vemos que en el condicional
slo tratamos dos alternativas, la tercera no necesita paso de registros. No hemos
usado la funcin output_asm_insn para producir cdigo, sino la macro ASM_OUT,
que no es sino un envoltorio (wrapper en ingls) de dicha funcin con el parmetro
de operandos por defecto.
#define ASM_OUT(a) output_asm_insn(a, operands)
Con esta macro mejoramos la lectura y ayuda a distinguir con ms facilidad las
lneas de cdigo ensamblador donde intervienen parmetros diferentes a los que
toma el insn que est siendo procesado en este momento.
Vistos un ejemplo sencillo y otro de complejidad media, queda por ver un ejem-
plo algo ms complejo. No debera existir ningn impedimento en su comprensin
si tenemos en cuenta lo explicado al inicio de este captulo; sin embargo, veremos
el uso de algunas funciones propias del back-end de GCC que necesitarn una expli-
cacin adicional. El ejemplo es el insn abshi2:
(define_insn "abshi2"
[(set (match_operand:HI 0 "register_operand" "=v,v,v")
(abs:HI (match_operand:HI 1 "reg_or_int_operand" "i
,0,v")))]
""
{
rtx label = gen_label_rtx();
switch (which_alternative)
Compilador de C para el microcontrolador Microchip PIC18F4550 92
{
case 0:
ASM_OUT("movlw low %1");
ASM_OUT("movwf %0");
ASM_OUT("movlw high %1");
ASM_OUT("movwf %0+1");
break;
case 1:
break;
case 2:
ASM_OUT("movff %1, %0");
ASM_OUT("movff %1+1, %0+1");
break;
default:
break;
}
ASM_OUT("btfss %0+1, 7");
fputs("\tbra ", asm_out_file);
output_addr_const(asm_out_file, label);
fputs("\n", asm_out_file);
ASM_OUT("comf %0+1, F");
ASM_OUT("negf %0");
ASM_OUT("btfsc STATUS, C");
ASM_OUT("incf %0+1, F");
output_addr_const(asm_out_file, label);
fputs("\n", asm_out_file);
return "";
}
)
Observamos tres nuevas funciones que todava no han sido explicadas. Por un
lado, vemos una llamada a fputs con dos argumentos, uno de ellos, un descriptor de
Compilador de C para el microcontrolador Microchip PIC18F4550 93
archivo llamado asm_out_le. Este descriptor corresponde al archivo ensamblador de
salida. Todo lo que escribamos sobre l aparecer en el archivo de salida, por ello
tenemos la posibilidad de usar fputs o fprintf para formatear la salida como creamos
conveniente.
Por otro lado, tenemos otra funcin que devuelve un rtx con la que podemos
crear etiquetas nicas en el cdigo: gen_label_rtx. Con esta funcin obtenemos una
etiqueta nica que podremos usar all donde nos haga falta. Para utilizar esta eti-
queta en el cdigo se utiliza la tercera funcin todava no explicada: output_addr_const.
Esta funcin escribe en el archivo cuyo descriptor asociado se pasa como primer
argumento el nombre de la etiqueta que corresponde al rtx que se le pasa como
segundo argumento. Este segundo parmetro debe ser una estructura rtx que re-
presente una etiqueta de una direccin de memoria. En este ejemplo utilizamos
output_add_const dos veces: la primera para generar el texto de la etiqueta como
parmetro de la instruccin bra y la segunda para producir el mismo texto como
etiqueta del cdigo que estamos generando.
El cdigo generado por este patrn, para el caso de la segunda alternativa y
antes pasar por la fase de recarga es el siguiente:
btfss %0+1, 7
bra _L3
comf %0+1, F
negf %0, F
btfsc STATUS, C
incf %0+1, F
_L3
Con estos tres ejemplos queda vista la creacin de patrones para la descripcin
de una nueva arquitectura. A partir de este punto, el archivo de descripcin de
mquina no debe representar ningn problema ms all del cdigo ensamblador
generado para cada insn.
6.4. Especicacin
Como vimos al principio del captulo, el archivo pic18.h de especicacin de
la arquitectura no es ms que la manera de describirle a GCC los detalles de la
arquitectura objetivo, descritos en el captulo 5, a travs de las macros oportunas,
junto a los target hooks que, inspirados en la especicacin del back-end para i386,
hemos utilizado en pic18.c.
El archivo pic18.opt es el de especicacin de opciones para el compilador. En
Compilador de C para el microcontrolador Microchip PIC18F4550 94
l especicamos los modicadores que admitir nuestro back-end desde la lnea de
comandos. Para este trabajo slo ha sido necesario utilizar un tipo de entrada, el
registro de denicin de opcin, que tiene tres campos: nombre del modicador, pro-
piedades de la opcin y descripcin que mostrar pic18-gcc cuando se ejecute con la
opcin help.
mstack-size=
Target RejectNegative Joined Var(user_defined_stack_size) \
Init("16")
Specify stack size
mextra-alloc=
Target RejectNegative Joined Var(user_defined_extra_memory)
\
Init("0")
Specify memory size to reserve
mpic-model=
Target RejectNegative Joined Var(user_defined_pic_model) \
Init("18f4550")
Select PIC model
La propiedad Target indica que esta opcin es especca para este back-end. Re-
jectNegative especica que no habr una opcin negativa de esta opcin, es decir,
no existir la opcin no-opcion_de_ejemplo. La propiedad Joined exige que esta op-
cin tome un argumento obligatorio, el cual debe indicarse a continuacin de la
opcin, sin espacios en blanco entre medias. Var especica a GCC que debe guardar
el argumento de esta opcin en la variable indicada entre parntesis. Por ltimo,
la propiedad Init, como puede intuirse, especica el valor que tomar la variable
mencionada en caso de que esta opcin no forme parte de la lnea de comandos
utilizada para ejecutar el compilador.
Para el back-end de PIC18 hemos especicado tres opciones. La primera de ellas
es mstack-size, especica el tamao de la pila de datos, que, por defecto, ser de 16
bytes. La segunda es mextra-alloc, y su propsito es especicar el tamao de memo-
ria adicional que debe reservar GCC (til a la hora de declarar variables globales en
archivos de cdigo distintos al principal). La tercera opcin permitir, en un futu-
ro prximo, seleccionar para qu modelo de microcontrolador deber GCC generar
cdigo.
Las siguientes funciones son las encargadas de, una vez recogidos los valores
Compilador de C para el microcontrolador Microchip PIC18F4550 95
de la lnea de comandos, adecuar las variables internas para conseguir el cdigo
deseado. Podemos ver en el archivo pic18.c la implementacin, pero, en resumi-
das cuentas, revisamos que los parmetros sean los adecuados y modicamos los
valores a considerar. La primera funcin revisa y aplica los valores indicados y la
segunda adeca el uso de los registros; por ejemplo, reduciendo el nmero de los
mismos.
#undef TARGET_OPTION_OVERRIDE
#define TARGET_OPTION_OVERRIDE pic_override_options
#undef TARGET_CONDITIONAL_REGISTER_USAGE
#define TARGET_CONDITIONAL_REGISTER_USAGE \
pic_conditional_register_usage
Vemos en las siguientes lneas la forma de especicar el ensamblador y enlaza-
dor por defecto, as como las opciones adecuadas.
#ifndef DEFAULT_ASSEMBLER
# define DEFAULT_ASSEMBLER "/usr/bin/gpasm"
#endif
#ifndef DEFAULT_LINKER
# define DEFAULT_LINKER "/usr/bin/gplink"
#endif
#define CC1_SPEC "-P"
#define ASM_SPEC "-c %{mpic-model=
*
:-p %
*
} " \
"%{!mpic-model=
*
:-p 18F4550}"
#define LINK_SPEC "%{mpic-model=
*
:-s " \
"%:gplink_include_file()} " \
"%{!mpic-model=
*
:-s " \
"/usr/share/gputils/lkr/18f4550.lkr}"
#define LINK_LIBGCC_SPEC ""
#define LIB_SPEC ""
#define LIBGCC_SPEC ""
#define STARTFILE_SPEC "libgcc.a%s"
Las siguientes macros indican a GCC los detalles sobre los tamaos de cada tipo
de dato, la alineacin y el endianess de las palabras en memoria.
Compilador de C para el microcontrolador Microchip PIC18F4550 96
#define BITS_PER_UNIT 8
#define BITS_BIG_ENDIAN 0
#define BYTES_BIG_ENDIAN 0
#define WORDS_BIG_ENDIAN 0
#ifdef IN_LIBGCC2
#define UNITS_PER_WORD 4
#else
#define UNITS_PER_WORD 1
#endif
#define PARM_BOUNDARY 8
#define STACK_BOUNDARY 8
#define FUNCTION_BOUNDARY 8
#define BIGGEST_ALIGNMENT 8
#define EMPTY_FIELD_BOUNDARY 8
#define STRICT_ALIGNMENT 0
#define MAX_FIXED_MODE_SIZE 16
#define INT_TYPE_SIZE 16
#define SHORT_TYPE_SIZE 8
#define LONG_TYPE_SIZE 32
#define LONG_LONG_TYPE_SIZE 64
#define FLOAT_TYPE_SIZE 32
#define DOUBLE_TYPE_SIZE 32
#define LONG_DOUBLE_TYPE_SIZE 32
#define DEFAULT_SIGNED_CHAR 0
#define SIZE_TYPE "unsigned int"
#define PTRDIFF_TYPE "int"
#define WCHAR_TYPE_SIZE 16
El primer bloque establece que la unidad de memoria ser un byte, 8 bits; el
esquema de almacenamiento ser little-endian tanto para bits como para palabras
de memoria, y establece que la alineacin de palabras en memoria ser siempre por
bytes.
En el segundo bloque especicamos los tamaos, en bits, para los distintos tipos
de datos. Hemos seguido el estndar ANSI C. Estas opciones permiten hacer los
tipos de datos en C ms prximos a los tipos que permite el hardware pero, en el
caso particular de los microcontroladores PIC, todos son mltiplos de 8 bits.
Especial mencin merece la denicin de UNITS_PER_WORD, donde distingui-
mos entre compilaciones genricas y la compilacin de la biblioteca libgcc, la biblio-
Compilador de C para el microcontrolador Microchip PIC18F4550 97
teca de funciones matemticas de GCC. El motivo de tal distincin es conseguir que
los modos SI y DI de libgcc2.c compilen de forma adecuada, dado que varias fun-
ciones utilizan modos superiores para trabajar en modos soportados, impidiendo
la compilacin si no hay espacio de palabra para los modos superiores, pese a que
dichos modos nunca se vayan a utilizar.
Continuamos con la especicacin de los registros.
#define PIC_FREG_NUM 32
#define PIC_RETURN_REGISTER (PIC_FREG_NUM / 2)
#define FIRST_PSEUDO_REGISTER 35
#define FIXED_REGISTERS { \
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /
*
F0 .. F15
*
/ \
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /
*
F16 .. F31
*
/ \
1,1,1} /
*
StackPointer, FramePointer, ArgPointer
*
/
#define CALL_USED_REGISTERS { \
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /
*
F0 .. F15
*
/ \
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, /
*
F16 .. F31
*
/ \
1,1,1} /
*
StackPointer, FramePointer, ArgPointer
*
/
#define REG_ALLOC_ORDER { \
16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,\
0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11,12,13,14,15,\
32,33,34}
Con FIRST_PSEUDO_REGISTERespecicamos el primer registro que no es real.
Al especicar 35, tenemos 35 registros que GCC considerar reales. De estos regis-
tros, 32 sern de propsito general y 8 bits de tamao, y 3 de propsito especco
(frame pointer, stack pointer y argument pointer). El tamao de los tres registros de pro-
psito especco ser 16 bits, frente a los 8 bits de los registros de propsito general.
Esto se deber a que su propsito es actuar como punteros a memoria de datos. En
FIXED_REGISTERS especicamos a GCC qu registros son de propsito especco
y no debe utilizar como genricos (aquellos marcados con un 1). Los registros mar-
cados con 1 en CALL_USED_REGISTERS sern aquellos cuyo valor sobrevive a un
cambio de marco de funcin; es decir, a una entrada o salida de una funcin. Para
aquellos que estn marcados con 0, GCC se encargar de producir el cdigo necesa-
rio para salvar estos registros antes de ejecutar una llamada a funcin y restaurarlos
al nalizar la ejecucin de la misma. Con REG_ALLOC_ORDER especicamos el or-
den en que GCC debe asignar los registros fsicos.
enum reg_class { NO_REGS,
Compilador de C para el microcontrolador Microchip PIC18F4550 98
VIRTUAL_REGS,
GENERAL_REGS,
ALL_REGS,
LIM_REG_CLASSES };
#define N_REG_CLASSES (int) LIM_REG_CLASSES
#define REG_CLASS_NAMES {"NO_REGS", \
"VIRTUAL_REGS", \
"GENERAL_REGS", \
"ALL_REGS"}
#define REG_CLASS_CONTENTS {{0x0,0x0}, \
{0xFFFFFFFF,0x7}, \
{0xFFFFFFFF,0x7}, \
{0xFFFFFFFF,0x7}}
#define REGNO_REG_CLASS(regno) (VIRTUAL_REGS)
#define BASE_REG_CLASS VIRTUAL_REGS
#define INDEX_REG_CLASS NO_REGS
GCC usa clases de registros para utilizar los adecuados en los patrones de las
insn. La macro enum_class especica las clases de registros que utilice nuestro back-
end. A continuacin, N_REG_CLASSES especica el nmero de clases de registros
existentes. REG_CLASS_NAMES es la macro donde especicamos los nombres sim-
blicos de estas clases de registros. REGNO_REG_CLASS es una macro que toma un
registro como argumento y devuelve a qu clase pertenece. Dado que nuestro back-
end slo utiliza la clase VIRTUAL_REGS, la macro REGNO_REG_CLASS siempre se
evalar como el valor de sta.
#define SMALL_REGISTER_CLASSES 1
#define REG_CLASS_FROM_LETTER(C) \
(C==v? VIRTUAL_REGS : NO_REGS)
#define REGISTER_NAMES { \
"F_REG","F_REG+.01","F_REG+.02","F_REG+.03", \
"F_REG+.04","F_REG+.05","F_REG+.06","F_REG+.07", \
"F_REG+.08","F_REG+.09","F_REG+.10","F_REG+.11", \
"F_REG+.12","F_REG+.13","F_REG+.14","F_REG+.15", \
"F_REG+.16","F_REG+.17","F_REG+.18","F_REG+.19", \
"F_REG+.20","F_REG+.21","F_REG+.22","F_REG+.23", \
Compilador de C para el microcontrolador Microchip PIC18F4550 99
"F_REG+.24","F_REG+.25","F_REG+.26","F_REG+.27", \
"F_REG+.28","F_REG+.29","F_REG+.30","F_REG+.31", \
"Stack_pointer", "Frame_pointer", "Arg_pointer"}
La macro SMALL_REGISTER_CLASSES indica el nmero de subclases de regis-
tros disponible en esta arquitectura. Como el nmero es sucientemente bajo, GCC
aplicar optimizaciones adecuadas para arquitecturas con pocas clases. La macro
REG_CLASS_FROM_LETTER nos permite identicar la clase de registros virtuales
mediante la letra v en las restricciones de los patrones de instruccin. La ltima ma-
cro del bloque anterior, REGISTER_NAMES, corresponde a un vector de cadenas
de caracteres que almacena el nombre de cada registro fsico.
Seguimos con las macros que indican como los valores quedan introducidos en
los registros.
#define PREFERRED_RELOAD_CLASS(x,class) VIRTUAL_REGS
#define HARD_REGNO_NREGS(regno, mode) \
((GET_MODE_SIZE(mode) + UNITS_PER_WORD - 1) \
/ UNITS_PER_WORD)
#define HARD_REGNO_MODE_OK(regno, mode) 1
#define MODES_TIEABLE_P(mode1, mode2) 1
#define BASE_REG_CLASS VIRTUAL_REGS
#define INDEX_REG_CLASS NO_REGS
#define REGNO_OK_FOR_BASE_P(num) \
(num < FIRST_PSEUDO_REGISTER)
#define REGNO_OK_FOR_INDEX_P(num) 0
#define CLASS_MAX_NREGS(class,mode) \
((GET_MODE_SIZE (mode) + UNITS_PER_WORD - 1) \
/ UNITS_PER_WORD)
PREFERRED_RELOAD_CLASS especica que durante la fase de recarga slo
deber asignar registros pertenecientes a la clase de registros virtuales que deni-
mos anteriormente. Con HARD_REGNO_NREGS especicamos el nmero de re-
gistros a partir de regno que hacen falta para almacenar un valor de tipo mode.
HARD_REGNO_MODE_ OK devuelve 1 si un valor de tipo mode se puede alma-
cenar en el registro regno: puesto que slo tenemos una clase de registros, en los
cuales se puede almacenar cualquier valor, esta macro siempre devuelve 1. La ma-
cro MODES_TIEABLE_P indica que nuestra mquina permite acceder a un valor de
un registro sea cual sea el modo de acceso o el del registro. Las siguientes macros
Compilador de C para el microcontrolador Microchip PIC18F4550 100
del bloque anterior slo especican para qu registros est permitida, o no, la fun-
cin indicada; pero dado que solo tenemos una clase de registros, no tienen ningn
inters.
Las tres macros siguientes determinan el comportamiento de la pila.
#define STACK_PUSH_CODE POST_INC
#define STACK_POINTER_OFFSET 0
#define PUSH_ROUNDING(npushed) npushed
Con estas macros establecemos una pila con post-incremento, sin offset respecto
a la cabeza de la pila y sin datos extra almacenados al apilar un valor: si apilamos
un dato de dos bytes, la pila aumenta su tamao exactamente en dos bytes.
A continuacin mostramos las macros y hooks que determinan cmo debe gene-
rar GCC el cdigo para las llamadas a funcin.
#define STARTING_FRAME_OFFSET 0
#define FIRST_PARM_OFFSET(fundec1) 0
#define RETURN_ADDR_RTX(count, frameaddr) \
gen_rtx_MEM(Pmode, \
memory_address(Pmode, \
plus_constant(tem,1))
#undef TARGET_RETURN_POPS_ARGS
#define TARGET_RETURN_POPS_ARGS pic_return_pops_args
#undef TARGET_FUNCTION_ARG
#define TARGET_FUNCTION_ARG pic_function_arg
#undef TARGET_FUNCTION_ARG_ADVANCE
#define TARGET_FUNCTION_ARG_ADVANCE pic_function_arg_advance
#define CUMULATIVE_ARGS int
#define INIT_CUMULATIVE_ARGS(cum, fntype, libname, \
fndec1, n_named_args) \
(cum = PIC_FREG_NUM - 1)
#define FUNCTION_ARG_REGNO_P(regno) \
(regno < FIRST_PSEUDO_REGISTER)
#undef TARGET_FUNCTION_VALUE
Compilador de C para el microcontrolador Microchip PIC18F4550 101
#define TARGET_FUNCTION_VALUE pic_function_value
#undef TARGET_FUNCTION_VALUE_REGNO_P
#define TARGET_FUNCTION_VALUE_REGNO_P \
pic_function_value_regno_p
#define LIBCALL_VALUE libcall_value
Las dos primeras macros de este bloque indican que no hay offset ni para el
marco de una funcin respecto al frame pointer, ni para los parmetros pasados a
una funcin respecto al argument pointer. Con RETURN_ADDR_RTX indicamos el
insn que se generar para salir de funciones con un tamao de marco dado. Con
el siguiente target hook, TARGET_RETURN_POPS_ARGS, que siempre devuelve 0,
indicaremos que no se sacarn automticamente valores de la pila al salir de una
funcin.
Las siguientes cinco macros y target hooks sirven para indicar en qu registros son
pasados los parmetros a una funcin. En realidad, el peso de esta decisin recae
sobre la funcin pic_function_arg, denida en pc18.c, la cual devuelve el registro a
partir del que debemos pasar el dato a una funcin, para un modo concreto.
Las ltimas tres macros y target hooks del bloque anterior indican dnde se de-
volver el valor de retorno de una funcin. Las funciones, en pic18.c, determinan
si el tipo de estos valores es simple o complejo; y, en funcin de esto, devolvern
un registro si se trata de un dato simple, o un apuntador a la posicin de memoria
donde est almacenado el dato si se trata de un tipo de dato complejo.
Continuamos con ms macros y target hooks de la especicacin.
#define STACK_POINTER_REGNUM (FIRST_PSEUDO_REGISTER - 3)
#define FRAME_POINTER_REGNUM (FIRST_PSEUDO_REGISTER - 2)
#define ARG_POINTER_REGNUM (FIRST_PSEUDO_REGISTER - 1)
#undef TARGET_FRAME_POINTER_REQUIRED
#define TARGET_FRAME_POINTER_REQUIRED \
pic_frame_pointer_required
#define DEFAULT_PCC_STRUCT_RETURN 0
#define EPILOGUE_USES(regno) 0
Las tres primeras macros de esta lista indican los registros que utilizar GCC
para cada puntero especco: pila, marco y argumentos de funcin. El target hook
TARGET_FRAME_POINTER_REQUIRED almacenar un puntero a una funcin la
Compilador de C para el microcontrolador Microchip PIC18F4550 102
cual determinar si la funcin que se va a llamar necesita un frame pointer o no,
que en nuestra especicacin siempre devuelve true. El propsito de que la siguien-
te macro, DEFAULT_PCC_STRUCT_RETURN, valga 1 es que el retorno de tipos
complejos se efectue mediante un puntero a memoria. La macro EPILOGUE_USES
determina si el eplogo de una funcin utiliza el registro que se le pasa como argu-
mento; como en nuestro diseo los eplogos nunca usan ningn registro, esta macro
siempre vale 0.
#define CONSTANT_ADDRESS_P(x) pic_constant_address_p(x)
#define MAX_REGS_PER_ADDRESS 2
#define REGISTER_MOVE_COST(mode,from,to) 1
#define MEMORY_MOVE_COST(mode,class,in) 10
#define BRACH_COST 1
CONSTANT_ADDRESS_P determina si el rtx corresponde a una direccin de
memoria constante. MAX_REGS_PER_ADDRESS especica que una direccin de
memoria estar compuesta, a lo sumo, por dos bytes. Este bloque de macros con-
cluye con las macros de coste de diversas acciones, que permiten a GCC elegir la
mejor opcin cuando hay varias disponibles.
Las macros siguientes indican a GCC qu tiene que escribir en el archivo de
salida para los distintos valores y secciones internas.
#define TEXT_SECTION_ASM_OP pic_text_section()
#define DATA_SECTION_ASM_OP pic_data_section()
#define BBS_SECTION_ASM_OP pic_bbs_section()
#define READONLY_DATA_SECTION_ASM_OP pic_rodata_section()
#define TARGET_ASM_FUNCTION_RODATA_SECTION \
default_no_function_rodata_section
#define TARGET_ASM_FILE_START_FILE_DIRECTIVE true
#define TARGET_ASM_FILE_START pic_target_asm_file_start
#define TARGET_ASM_FILE_END pic_target_asm_file_end
#define ASM_COMMENT_START ";"
#define ASM_APP_ON ";APP\n"
#define ASM_APP_OFF ";NO_APP\n"
#define TARGET_HAVE_NAMED_SECTIONS false
#define TARGET_ASM_UNALIGNED_HI_OP NULL
Compilador de C para el microcontrolador Microchip PIC18F4550 103
#define TARGET_ASM_UNALIGNED_SI_OP NULL
#define ASM_OUTPUT_ASCII(stream, ptr, len) \
pic_output_ascii(stream, ptr, len)
#define IS_ASM_LOGICAL_LINE_SEPARATOR(C, STR) \
((C) == \n || (C) == $)
#define ASM_OUTPUT_LOCAL(x,y,z,k) \
pic_asm_output_local(x,y,z,k)
#define ASM_OUTPUT_COMMON(x,y,z,w) \
pic_asm_output_common(x,y,z,w)
#define ASM_OUTPUT_EXTERNAL(x,y,z) \
fprintf(x, "\t extern\t_%s\n",z)
#define SIZE_ASM_OP "\tres\t"
#define ASM_DECLARE_FUNCTION_NAME(stream, name, decl) \
pic_asm_declare_function_name(stream, name,decl)
#define ASM_DECLARE_OBJECT_NAME(stream, name, decl) \
pic_asm_declare_object_name(stream, name, decl)
#define GLOBAL_ASM_OP "\tglobal\t_"
#define ASM_GENERATE_INTERNAL_LABEL(STRING, PREFIX, NUM) \
do { \
snprintf(STRING, 100, "_%s%lu", \
PREFIX, (unsigned long)(NUM)); \
last_fsr=0; \
} while (0)
#define HAS_INIT_SECTION
#define PRINT_OPERAND(stream, x, code) \
pic_print_operand(stream, x, code)
#define PRINT_OPERAND_ADDRESS(stream, x) \
pic_print_operand_address(stream, x)
#define ASM_OUTPUT_REG_PUSH(stream, regno) \
if (regno!=0) \
abort(); \
else \
fprintf(stream, "\tPUSH");
Compilador de C para el microcontrolador Microchip PIC18F4550 104
#define ASM_OUTPUT_REG_POP(stream, regno) \
if (regno != 0) \
abort(); \
else \
fprintf(stream, "\tPOP");
#define ASM_OUTPUT_SKIP(stream, nbytes) \
pic_asm_output_skip(stream, nbytes)
#define ASM_OUTPUT_ALIGN(stream, nbytes) \
fprintf(stream, ";ALIGN")
#define DBX_DEBUGGING_INFO
#define ASM_STABS_OP "\t;.stabs\t"
#define ASM_STABD_OP "\t;.stabd\t"
#define ASM_STABN_OP "\t;.stabn\t"
#define ASM_OUT(a) output_asm_insn(a, operands);
Con todas las explicaciones de esta seccin hemos querido mostrar los puntos
ms importantes de la especicacin. Para ampliar la informacin de las macros
y target hooks utilizados, recomendamos consultar la mejor fuente de informacin
existente sobre GCC, el libro GCC Internals, que se distribuye junto con el cdigo
fuente de GCC y es posible encontrar una versin online en la web del proyecto:
http://gcc.gnu.org/onlinedocs/gccint.
Captulo 7
Anlisis del cdigo generado
Durante este captulo vamos a ver cmo las sentencias bsicas en C pasan, gra-
cias a nuestra especicacin y los patrones dados, a cdigo ensamblador para el
PIC18F4550.
A continuacin, mostraremos bloques de cdigo concretos para mostrar todos
los datos que nuestro compilador escribe en el archivo ensamblador de salida resul-
tante del proceso de compilacin de un programa C.
Los cdigos mostrados son los que genera GCC de manera directa, sin ninguna
optimizacin del middle-end. De esta forma resultar ms sencillo comprender c-
mo se realiza la traduccin de patrones. En el da a da de cualquier programador,
lo habitual es aadir opciones de optimizacin que reducen el tamao del cdigo
generado, lo hacen ms rpido de ejecutar o incluso eliminan partes de l si GCC
predice que no llegarn a ejecutarse.
7.1. Asignacin
Comenzamos con las sentencias de asignacin. Algo tan sencillo como la si-
guiente sentencia:
int x = 10;
se transforma en la secuencia de instrucciones en ensamblador que realizan la
operacin equivalente. Para acceder a la memoria, GCC hace uso del puntero de
marco de funcin (frame pointer). Primero carga el valor del frame pointer en una de
las parejas de registros de acceso indirecto y, acto seguido, escribe el nuevo valor
de la variable en la posicin de memoria asignada. Debido a que se trata de una
variable de tipo int, de tamao 2 bytes, la asignacin consta de dos escrituras de
1 byte: la primera de ellas tomar el valor bajo del valor constante (macro low) y
105
Compilador de C para el microcontrolador Microchip PIC18F4550 106
escribir en el registro WREG; de ah pasar a escribirse en la posicin de memoria
apuntada por FSR0, a travs de INDF0; despues de esto, incrementar FSR0 para
que apunte a la siguiente posicin de memoria y escribir la parte alta del valor
constante (macro high). En este caso particular encontramos que la parte alta del
valor 10 es 0, por lo que no hay necesidad de cargar el registro WREGy luego copiar
a INDF0: es posible reemplazar esas dos instrucciones por una sola que ponga a cero
el registro destino.
movff Frame_pointer+1, FSR0H
movff Frame_pointer, FSR0L
movlw low D10
movwf INDF0
infsnz FSR0L, F
incf FSR0H, F
clrf INDF0
Si en la declaracin de la variable no hubiese una asignacin, GCC no emitira
cdigo ensamblador correspondiente, tan slo se limitara a llevar la cuenta para
s mismo de cuanto espacio de memoria debe reservar para este marco de funcin,
pero no producira cdigo ensamblador.
El cdigo anterior es exactamente el mismo que el que GCC producir para las
instrucciones siguientes:
int x;
x = 10;
Aunque hubiese otras instrucciones entre la declaracin de la variable y la pos-
terior asignacin a partir de un valor constante, el compilador slo emitir cdigo
ensamblador en el punto del programa correspondiente a la asignacin.
La declaracin de ms de una variable en un mismo marco de funcin resulta
en un manejo ms complejo, por parte de GCC, del frame pointer.
int x = 10;
int y = 25;
El resultado de compilar las dos sentencias anteriores es un cdigo como el si-
guiente:
movff Frame_pointer+1, FSR0H
movff Frame_pointer, FSR0L
movlw low D10
Compilador de C para el microcontrolador Microchip PIC18F4550 107
movwf INDF0
infsnz FSR0L, F
incf FSR0H, F
clrf INDF0
movff Frame_pointer, F_REG+.16
movff Frame_pointer+1, F_REG+.16+1
movlw low D2
addwf F_REG+.16, F
movlw high D2
addwfc F_REG+.16+1, F
movff F_REG+.16+1, FSR0H
movff F_REG+.16, FSR0L
movlw low D25
movwf INDF0
infsnz FSR0L, F
incf FSR0H, F
clrf INDF0
Por claridad, este bloque de cdigo est dividido en tres bloques ms pequeos,
cada uno de ellos con un propsito concreto.
Las siete primeras instrucciones son anlogas a las del ejemplo anterior: cargan
en un registro de acceso indirecto la direccin base del marco de esta funcin y, a
continuacin, almacenan en dicha posicin los dos bytes que componen el valor de
la variable x, de tipo int.
El segundo bloque de cdigo carga la direccin base del marco de funcin en
registros de propsito general para, acto seguido, incrementarla en dos unidades
(los dos bytes que corresponden a la variable x, que ya se encuentran asignados).
El ltimo bloque es muy semejante al primero. Dejando a un lado la diferen-
cia obvia de que almacena un valor distinto en la memoria direccionada mediante
FSR0, lo ms destacable de este bloque es que copia en la pareja de registros FSR0
el valor que se calcul en el bloque dos, valor que corresponde a la posicin de
memoria asignada a la variable y, contigua a x.
De estos ejemplos se puede ver que el cdigo producido por GCC es bastante
redundante y simple. Una forma ms eciente de hacer las asignaciones anteriores
pasa por almacenar las posiciones de memoria absolutas directamente en registros
y evitar con esto el tener que calcular las relativas al frame pointer cada vez que haya
que acceder de nuevo a esas posiciones. Debemos recordar que estos ejemplos son
Compilador de C para el microcontrolador Microchip PIC18F4550 108
el resultado de la traduccin de C a ensamblador sin ninguna optimizacin. Con un
nivel mnimo de optimizaciones, GCC probablemente ni siquiera cargar esos valo-
res constantes (a menos que cambiase su valor en algn momento del programa),
dado que sabe que los accesos a memoria son muy costosos y, desde el nivel ms ba-
jo de optimizacin, procura reducirlos al mnimo y emplear registros siempre que
sea posible.
7.2. Operaciones matemticas simples
7.2.1. Suma
Pasamos a estudiar las operaciones matemticas. Todas tienen una forma pare-
cida, a pesar de que las instrucciones en ensamblador empleadas vara de una a
otra.
Comenzamos con la operacin de suma. Supongamos que x e y son variables de
tipo entero, y que ya han sido declaradas e inicializadas con valores arbitrarios.
x = x + y;
El cdigo generado para la sentencia C anterior es:
movff Frame_pointer, F_REG+.16
movff Frame_pointer+1, F_REG+.16+1
movlw low D2
addwf F_REG+.16, F
movlw high D2
addwfc F_REG+.16+1, F
movff Frame_pointer+1, FSR0H
movff Frame_pointer, FSR0L
movff INDF0, F_REG+.18
infsnz FSR0L, F
incf FSR0H, F
movff INDF0, F_REG+.18+1
movff F_REG+.16+1, FSR0H
movff F_REG+.16, FSR0L
movff INDF0, F_REG+.16
Compilador de C para el microcontrolador Microchip PIC18F4550 109
infsnz FSR0L, F
incf FSR0H, F
movff INDF0, F_REG+.16+1
movf F_REG+.18, W
addwf F_REG+.16, F
movf F_REG+.18+1, W
addwfc F_REG+.16+1, F
movff Frame_pointer+1, FSR0H
movff Frame_pointer, FSR0L
movff F_REG+.16, INDF0
infsnz FSR0L, F
incf FSR0H, F
movff F_REG+.16+1, INDF0
Examinando por bloques, observamos cmo en los dos primeros bloques se pre-
paran el apuntador a la variable cuya posicin de memoria corresponde a dos bytes
ms all de la base del frame pointer. Este puntero queda almacenado en los registros
16 y 17.
El cdigo del bloque tres carga en los registros 18 y 19 el contenido de la variable
cuya memoria corresponde con la base del frame pointer. Una vez hecho esto, el
bloque cuatro, haciendo uso del apuntador obtenido en los dos primeros bloques,
carga en los registros 16 y 17 el valor de la otra variable que interviene en la suma,
destruyendo el apuntador durante el proceso.
El quinto bloque es el que efecta la operacin de suma. En l aparecen dos
instrucciones de suma. Esto se debe a que los sumandos son de 2 bytes cada uno
y el PIC18 trabaja con palabras de datos de 1 byte, por lo que hay que tener en
cuenta el bit de acarreo despus de hacer la suma de los bytes menos signicativos
de cada sumando y sumarlo al resultado de sumar los dos bytes ms signicativos.
Los bytes resultantes de la suma quedan almacenados en los registros 16 y 17.
El ltimo bloque es el que almacena en memoria, en la primera variable del
marco de la funcin actual, el resultado de la suma efectuada en el bloque anterior.
Es conveniente destacar que la eleccin de registros la hace GCC, teniendo en
cuenta unas reglas de preferencia que le hemos especicado y, por supuesto, los que
se encuentran disponibles para utilizar. Tambin merece la pena llamar la atencin
sobre que, a priori, no podramos asegurar en qu posiciones del marco de funcin
se encuentrar cada variable implicada. En cambio, una vez analizado el cdigo
vemos que la variable x es la que se encuentra en la direccin apuntada por el frame
Compilador de C para el microcontrolador Microchip PIC18F4550 110
pointer mientras que a y corresponden los bytes 3 y 4, tomando como referencia el
mismo puntero.
7.2.2. Resta
Veamos el caso de la operacin de resta.
x = x - y;
El cdigo generado para esta sentencia es:
movff Frame_pointer, F_REG+.16
movff Frame_pointer+1, F_REG+.16+1
movlw low D2
addwf F_REG+.16, F
movlw high D2
addwfc F_REG+.16+1, F
movff Frame_pointer+1, FSR0H
movff Frame_pointer, FSR0L
movff INDF0, F_REG+.18
infsnz FSR0L, F
incf FSR0H, F
movff INDF0, F_REG+.18+1
movff F_REG+.16+1, FSR0H
movff F_REG+.16, FSR0L
movff INDF0, F_REG+.16
infsnz FSR0L, F
incf FSR0H, F
movff INDF0, F_REG+.16+1
movff F_REG+.18+1, F_REG+.20+1
movff F_REG+.18, F_REG+.20
movf F_REG+.16, W
subwf F_REG+.20, F
movf F_REG+.16+1, W
Compilador de C para el microcontrolador Microchip PIC18F4550 111
btfsc STATUS, N
incfsz F_REG+.16+1, W
subwf F_REG+.20+1, F
movff F_REG+.20, F_REG+.16
movff F_REG+.20+1, F_REG+.16+1
movff Frame_pointer+1, FSR0H
movff Frame_pointer, FSR0L
movff F_REG+.16, INDF0
infsnz FSR0L, F
incf FSR0H, F
movff F_REG+.16+1, INDF0
El cdigo que nos interesa es el del sexto bloque, que es el que efecta la ope-
racin de resta de los dos valores. El acceso a los valores en memoria, carga de los
registros (elegidos por GCC) y el posterior almacenamiento en memoria del resul-
tado, siempre tiene la misma forma.
Puesto que el cdigo de accesos a memoria tanto para carga como almacena-
miento de los registros se repite en todas las operaciones y ya ha sido descrito con
sucientes detalles, a partir de aqu obviaremos dicho cdigo y nos centraremos en
el bloque de cdigo que efecta la operacin a tratar.
Volvamos a la resta, quedndonos con la parte interesante:
movf F_REG+.16, W
subwf F_REG+.20, F
movf F_REG+.16+1, W
btfsc STATUS, N
incfsz F_REG+.16+1, W
subwf F_REG+.20+1, F
En las dos primeras lneas se efecta la resta de la parte baja del sustraendo
(contenido en el registro 16) al minuendo (almacenado en el registro 20). El resulta-
do queda alojado en este ltimo registro. A continuacin se efecta la resta de los
bytes ms signicativos, estando el MSB del minuendo en el registro 21 y el MSB
del sustraendo en el 17. Antes de ejecutar la instruccin de resta se comprueba si la
resta de los bytes menos signicativos produjo acarreo. En caso armativo, incre-
mentar en uno el MSB del sustraendo antes de la instruccin de resta.
Compilador de C para el microcontrolador Microchip PIC18F4550 112
7.2.3. Negacin
El operador unario de negacin puede implementarse de varias maneras. La im-
plementacin elegida para nuestro back-end hace uso de las instrucciones de com-
plemento.
La siguiente sentencia C:
x = -y;
resulta en el siguiente cdigo ensamblador:
comf F_REG+.16+1, F
negf F_REG+.16
btfsc STATUS, C
incf F_REG+.16+1, F
El bloque de cdigo que efecta la operacin de negacin (complemento a 2)
trabaja de arriba hacia abajo, es decir, comienza por los bytes ms signicativos,
haciendo su complemento a 1, hasta llegar al LSB, el cual complementa a 2 y luego
propaga el acarreo byte a byte hasta llegar al MSB. Este mismo algoritmo sirve para
nmeros enteros de cualquier tamao.
7.2.4. Valor absoluto
La operacin de valor absoluto, como concepto, resulta muy sencilla pero por
desgracia el PIC18 no ofrece ninguna instruccin que facilite su implementacin.
Sin embargo, es posible implementarla tal como la ensean en una clase de mate-
mticas de nivel bsico: si el signo es negativo, se lo cambiamos; si no, se deja tal cual.
As, haciendo uso de las instrucciones empleadas para la negacin y una sencilla
comprobacin del bit de signo tenemos implementada esta operacin.
x = abs(y);
Este ejemplo se traduce en el siguiente cdigo ensamblador:
btfss F_REG+.16+1, 7
bra _L2
comf F_REG+.16+1, F
negf F_REG+.16
btfsc STATUS, C
incf F_REG+.16+1, F
_L2
Compilador de C para el microcontrolador Microchip PIC18F4550 113
La primera lnea comprueba el bit ms signicativo del MSB que compone el
valor. En caso de que ste valga uno, salta una nica instruccin, pasando a ejecutar
las lneas tres y sucesivas, que efectan la negacin del nmero. Si el bit de signo
vale cero, ejecuta la instruccin de salto que carga PC con la direccin de la etiqueta
ubicada al nal del bloque, dando por nalizada la operacin de valor absoluto.
7.3. Operaciones lgicas
7.3.1. AND, OR y XOR
El PIC18 puede realizar la mayora de operaciones lgicas directamente. Una vez
que tenga el dato en un registro basta con realizar la operacin sobre el mismo. Con
esta facilidad es posible realizar las operaciones de disyuncin (AND), conjuncin
(OR) y exclusin (XOR) lgicas. Veamos un ejemplo ilustrativo de una operacin
AND sobre un valor de tipo entero.
x = x & y;
Obviando el cdigo correspondiente a la carga y almacenamiento de los regis-
tros empleados por el compilador, el cdigo resultante de traducir a ensamblador
esta sentencia C es el siguiente:
movf F_REG+.18, W
andwf F_REG+.16, F
movf F_REG+.18+1, W
andwf F_REG+.16+1, F
Las otras operaciones son idnticas en forma. Las instrucciones andwf, pasan a
ser iorwf para la disyuncin y xorwf si se trata de una operacin de exclusin lgica.
7.3.2. Negacin Lgica o Complemento a 1
El caso del complemento a uno es sensiblemente distinto a las anteriores ope-
raciones, ya que al tratarse de un operador unario puede suceder que GCC decida
emplear el mismo registro como entrada y como salida, por lo que el cdigo resul-
tante suele ser algo ms corto que para las operaciones de dos operandos.
x = ~y;
El cdigo que nos interesa, resultante de compilar la negacin de este ejemplo,
es el siguiente:
Compilador de C para el microcontrolador Microchip PIC18F4550 114
comf F_REG+.16, F
comf F_REG+.16+1, F
7.3.3. Desplazamientos
El desplazamiento a nivel de bits es otra operacin complicada en un microcon-
trolador PIC debido a lo reducido de su conjunto de instrucciones. A priori, uno
puede pensar en dos tipos posibles: desplazamiento hacia la izquierda y desplaza-
miento hacia la derecha. No obstante, en el desplazamiento hacia la derecha hay
dos variantes, dependiendo de si se trata de un valor con o sin signo, y su imple-
mentacin es diferente.
int x = 10;
char y = 2;
x = x >> y;
El operador de desplazamiento, para este ejemplo, produce el cdigo siguiente:
movf F_REG+.16, W
bra _L3
_L2
bcf STATUS, C
btfsc F_REG+.20+1, 7
bsf STATUS, C
rrcf F_REG+.20+1, F
rrcf F_REG+.20, F
addlw 0xFF
_L3
btfss STATUS, Z
bra _L2
El PIC18 slo dispone de instrucciones para desplazar un nico bit, por lo que
hay que emplear un contador y un bucle que se ejecuta mientras el contador no haya
llegado a cero. El cdigo resultante emplea WREGcomo contador y en la instruccin
ubicada en la segunda etiqueta es donde efecta la comprobacin: si WREG no es
cero, salta a la primera etiqueta y carga en el bit de acarreo el valor del bit de mayor
peso; una vez hecho esto, hace el desplazamiento y decrementa en una unidad el
valor del contador.
Para desplazamientos hacia la derecha de valores sin signo no es necesario com-
probar el bit ms signicativo del dato, ya que siempre se introduce un cero. As
Compilador de C para el microcontrolador Microchip PIC18F4550 115
que basta con asegurarse de poner a cero el bit de acarreo, que es el que entra por
la izquierda cada vez que se ejecuta una instruccin de desplazamiento hacia la
derecha.
unsigned int x = 10;
char y = 2
x = x >> y;
Este ejemplo se traduce en el siguiente cdigo:
movf F_REG+.16, W
bra _L3
_L2
bcf STATUS, C
rrcf F_REG+.20+1, F
rrcf F_REG+.20, F
addlw 0xFF
_L3
btfss STATUS, Z
bra _L2
Como podemos ver, la nica diferencia con el desplazamiento de valores con
signo es que en el cuerpo del bucle no se efecta la comprobacin del bit 7 del MSB.
En los desplazamientos hacia la izquierda no existe ninguna diferencia entre va-
lores con o sin signo, ya que en ambos casos entra un cero por la derecha. El cdigo
resultante es idntico al desplazamiento hacia la derecha de valores sin signo, tan
slo cambiando las instrucciones rrcf por rlcf.
7.4. Conversin de tipos o casting
7.4.1. Extensin de signo
Normalmente, cuando queremos usar datos de mayor rango necesitamos usar
tipos nuevos. Por ejemplo, si queremos calcular un nmero por encima del valor
mximo de los enteros con signo, 32767, necesitaremos utilizar un tipo que lo per-
mita, como long (entero largo de 4 bytes), cuyo valor mximo es 2147483647. Para
convertir de uno al otro, en C hacemos uso de lo que se denomina casting de tipos.
int x = 10;
long y = x;
Compilador de C para el microcontrolador Microchip PIC18F4550 116
Para esta sencilla operacin, que bsicamente consiste en cargar en la variable y
el valor de x, necesitamos rellenar los bytes de la variable y, de cuatro bytes, que no
quedan cubiertos por la variable x, ya que sta es de slo dos bytes. GCC utiliza la
operacin de extensin de signo, necesaria entre todos los tipos a los que se quiera
tener la posibilidad de ampliacin. El cdigo generado para este ejemplo es:
movff Frame_pointer+1, FSR0H
movff Frame_pointer, FSR0L
movff INDF0, F_REG+.18
infsnz FSR0L, F
incf FSR0H, F
movff INDF0, F_REG+.18+1
movlw 0x00
btfsc F_REG+.18+1, 7
movlw 0xff
movwf F_REG+.18+2
movwf F_REG+.18+3
Como vemos, el primer bloque del resultado se encarga de cargar en registros
el valor de la variable de entrada, de slo dos bytes. Sin embargo, la variable de
salida, la asignada, es de cuatro bytes. El valor de los dos bytes ms signicativos
depender del bit de signo de la variable de entrada. El cometido del segundo blo-
que es comprobar el valor de dicho bit y extenderlo por los dos bytes adicionales que
conforman la variable asignada.
7.4.2. Extensin de ceros
La extensin de valores sin signo se denomina, dentro del proyecto GCC, zero-
extension que podemos traducirla como extensin de ceros. El algoritmo de la extensin
de ceros es tan sencillo como poner a cero los bytes de la variable asignada que no
corresponden con la variable de entrada.
unsigned int x = 10;
long y = x;
El resultado de la zero-extension anterior es:
movff Frame_pointer+1, FSR0H
movff Frame_pointer, FSR0L
Compilador de C para el microcontrolador Microchip PIC18F4550 117
movff INDF0, F_REG+.18
infsnz FSR0L, F
incf FSR0H, F
movff INDF0, F_REG+.18+1
clrf F_REG+.18+2
clrf F_REG+.18+3
Igual que ocurre con la extensin de signo, para la zero-extension primero se carga
en registros el valor de entrada. A continuacin, el segundo bloque de cdigo se
limita a poner a cero los registros asignados a los bytes de ms peso de la variable
de salida.
Dada la limitacin de memoria de un PIC, lo ms aconsejable es trabajar siem-
pre con el tipo de dato ms pequeo que permita el rango de valores necesario para
cada propsito; es un desperdicio de memoria, por ejemplo, emplear una variable
de tipo long para un dato ledo del conversor analgico-digital congurado a 8 bits,
cuando el tipo short permite almacenar todos los valores de ese rango. Lo aconseja-
ble es mantener el dato en una variable del tipo ms pequeo que pueda albergarlo
hasta que sea necesario, por el motivo que sea, un mayor rango numrico.
7.5. Operaciones matemticas complejas
7.5.1. Multiplicacin
Durante la etapa de diseo tomamos la decisin de implementar una solucin
combinada para la multiplicacin: para datos pequeos haremos uso del multipli-
cador hardware, por lo que el cdigo resultante es anlogo al producido para la
suma y la resta. Para datos grandes optaremos por hacer una llamada a la bibliote-
ca de funciones, de modo que el tamao de nuestro programa no creciese ms de lo
estrictamente necesario.
Veamos un ejemplo de multiplicacin de datos grandes.
long x;
int y = 10;
int z = 25;
x = y
*
z;
Esto se traduce en lo siguiente:
Compilador de C para el microcontrolador Microchip PIC18F4550 118
movff F_REG+.18+1, F_REG+.26+1
movff F_REG+.18, F_REG+.26
movff F_REG+.16+1, F_REG+.28+1
movff F_REG+.16, F_REG+.28
call _mulhisi
movff F_REG+.28, F_REG+.16
movff F_REG+.28+1, F_REG+.16+1
movff F_REG+.28+2, F_REG+.16+2
movff F_REG+.28+3, F_REG+.16+3
Las funciones matemticas tienen un comportamiento particular respecto a las
funciones denidas por el usuario en lo que se reere al paso de argumentos, pero
eso lo veremos ms adelante. Ahora centrmonos en las funciones matemticas de
nuestra biblioteca.
El paso de argumentos y el retorno del resultado es especco para cada funcin
de la biblioteca, y lo mismo sucede con las variables que emplea cada una de estas
funciones. GCC no decide sobre la marcha qu registros servirn para cada prop-
sito dentro de estas funciones, esta decisin es ja en el cdigo de la biblioteca y
GCC sabe cmo comunicarse con estas funciones, sin interferir en el buen funciona-
miento de stas y evitando que dichas funciones hagan lo propio con el cdigo que
s genera el compilador.
En el cdigo resultante del ejemplo que estamos tratando distinguimos cuatro
bloques. Los dos primeros cargan en los registros 26 y 27 uno de los operandos y en
los registros 28 y 29 el segundo operando. Esto es as porque la funcin mulhisi exige
que los operandos estn almacenados en esos registros. Al nalizar su ejecucin (la
llamada a mulhisi) encontraremos el resultado de la multiplicacin en los registros
28 a 31 (la eleccin de estos registros no la efecta GCC de manera dinmica, sino
que estn predenidos en el cuerpo de esta funcin). El cuarto bloque carga en los
registros asignados a la variable x el resultado de multiplicar y por z.
7.5.2. Divisin
Veamos ahora un ejemplo de la operacin de divisin.
int x;
int y = 10;
Compilador de C para el microcontrolador Microchip PIC18F4550 119
int z = 25;
x = y / z;
El resultado de traducir la divisin de dos nmeros enteros de 2 bytes es un
cdigo como ste:
movff F_REG+.18+1, F_REG+.30+1
movff F_REG+.18, F_REG+.30
movff F_REG+.16+1, F_REG+.28+1
movff F_REG+.16, F_REG+.28
call _divmodhi
movff F_REG+.30, F_REG+.16
movff F_REG+.30+1, F_REG+.16+1
Encontramos el mismo esqueleto que para la operacin de multiplicacin: carga
de un operando, carga del otro, llamada a la funcin de la biblioteca y recuperacin
del resultado deseado.
Las funciones de divisin, adems de las particularidades que tienen por tratarse
de cdigo ensamblador escrito a mano, son peculiares porque en realidad tienen un
doble cometido: obtener cociente y resto de una operacin de divisin. Una nica
llamada a una de estas funciones, como divmodhi, deja el valor del cociente resul-
tante en unos registros y el resto en otros, por lo que las sentencias en C de divisin
y mdulo resultan en un cdigo ensamblador prcticamente idntico, con la dife-
rencia de que, tras la llamada a funcin, cogern el resultado de unos registros u
otros.
7.6. Estructuras condicionales o de seleccin
7.6.1. if
Empezamos a examinar el cdigo generado para las estructuras de control. La
ms sencilla es la estructura de ejecucin condicional if. En este salto, la expresin
del if es evaluada para decidir si se ejecutar el bloque then siguiente o no.
La implementacin de las estructuras if tiene dos partes: comparacin y salto.
Cuando la condicin de la estructura if es una comparacin entre dos datos de un
Compilador de C para el microcontrolador Microchip PIC18F4550 120
byte, el cdigo ensamblador en el que se traduce tiene bien diferenciados los blo-
ques de comparacin y de salto. Sin embargo, cuando se trata de comparar, por
ejemplo, dos valores de tipo int, ambas partes se mezclan dando lugar a un peque-
o combinado de comparaciones, pequeos saltos y un gran salto, que es el que
marca el nal de la comparacin de la estructura if. Lo vemos con un ejemplo de
comparacin entre dos valores de tipo int.
if (x != y)
{
...
}
Esto se traduce en el cdigo siguiente:
movf F_REG+.16, W
xorwf F_REG+.18, W
btfss STATUS, Z
goto _L3
movf F_REG+.16+1, W
xorwf F_REG+.18+1, W
_L3 btfsc STATUS, Z
goto _L1
...
_L1
Los puntos suspensivos representan el bloque then, cuyo contenido es irrele-
vante para nuestra explicacin. No obstante, para mostrar dnde aparece el cdigo
correspondiente al bloque then, tambin los hemos includo en el ensamblador ge-
nerado por GCC.
En este ejemplo slo se ejecutar el bloque then si las dos variables evaluadas
son diferentes. GCC genera una comparacin de igualdad y un salto a la etiqueta
L1 en caso armativo, es decir, salta si no se cumple la condicin.
Las dos primeras lneas comparan, mediante la instruccin de exclusin lgica,
los bytes menos signicativos que componen las variables implicadas y si el resul-
tado es cero (bit Z del registro de estado puesto a uno), es que ambos son iguales
y hay que comparar los bytes ms signicativos; en caso contrario los valores son
distintos (no hay necesidad de comparar los otros dos bytes) y salta a la etiqueta L3,
donde se efecta la comparacin nal y evita, o no, la ejecucin del bloque then. En
Compilador de C para el microcontrolador Microchip PIC18F4550 121
la comparacin de los bytes altos, tras ejecutar la instruccin de exclusin lgica,
comprueba otra vez si el bit Z est puesto a cero y, en caso de que fuese as (porque
los valores sean iguales), ejecutara el salto incondicional goto hacia la etiqueta L1,
lo que signica no ejecutar el bloque then.
Por analoga, para el caso de que la comparacin fuese de igualdad, el cdigo
resultante sera exactamente igual en forma y tan slo cambiara la ltima compro-
bacin, que pasara a efectuar el salto incondicional si el bit Z no est puesto a cero.
btfss STATUS, Z
Existen otras ocho posibles comparaciones entre dos valores aparte de las dos
que acabamos de analizar. En realidad son slo cuatro, pero hay que distinguir entre
comparaciones de valores con signo y sin l. Estas comparaciones son mayor-que,
mayor-o-igual-que, menor-que y menor-o-igual-que.
La implementacin de estas comparaciones se apoyan en la instruccin de resta.
El procedimiento es tan sencillo como restar los operandos y comprobar el signo
del resultado.
if (x <= y)
{
...
}
Esta estructura se traduce en:
movf F_REG+.18, W
subwf F_REG+.16, W
movf F_REG+.18+1, W
btfss STATUS, C
incfsz F_REG+.18+1, W
subwf F_REG+.16+1, W
andlw 0x80
btfss STATUS, Z
goto _L1
...
...
...
_L1
Compilador de C para el microcontrolador Microchip PIC18F4550 122
El cdigo resultante de esta compilacin resta x a y y comprueba el bit de signo
resultante. En caso de que valga uno (resultado negativo, lo que implicaa que x es
mayor que y), ejecuta el cuerpo del bloque then. De lo contrario, ejecuta el goto hacia
la etiqueta L1. El cdigo para la comparacin mayor-o-igual-que es idntico a ste,
tan slo se intercambian los operandos.
Cuando la comparacin es una desigualdad estricta, el cdigo que resulta es
tambin muy parecido al de las desigualdades no-estrictas, teniendo en cuenta que
unas son complementarias de las otras basta cambiar la condicin de salto. As, el
cdigo para una comparacin A<B es el mismo que el de A>=B, pero cambiando la
comprobacin del bit Z, donde ahora hay que comprobar que valga cero. Para verlo
con ms claridad, el listado siguiente muestra el resultado de traducir la misma
estructura if anterior para cada una de las cuatro comparaciones:
; (x < y)
movf F_REG+.16, W
subwf F_REG+.18, W
movf F_REG+.16+1, W
btfss STATUS, C
incfsz F_REG+.16+1, W
subwf F_REG+.18+1, W
andlw 0x80
btfsc STATUS, Z
goto _L1
; (x > y)
movf F_REG+.18, W
subwf F_REG+.16, W
movf F_REG+.18+1, W
btfss STATUS, C
incfsz F_REG+.18+1, W
subwf F_REG+.16+1, W
andlw 0x80
btfsc STATUS, Z
goto _L1
; (x >= y)
movf F_REG+.16, W
subwf F_REG+.18, W
Compilador de C para el microcontrolador Microchip PIC18F4550 123
movf F_REG+.16+1, W
btfss STATUS, C
incfsz F_REG+.16+1, W
subwf F_REG+.18+1, W
andlw 0x80
btfss STATUS, Z
goto _L1
; (x <= y)
movf F_REG+.18, W
subwf F_REG+.16, W
movf F_REG+.18+1, W
btfss STATUS, C
incfsz F_REG+.18+1, W
subwf F_REG+.16+1, W
andlw 0x80
btfss STATUS,Z
goto _L1
Para valores sin signo, la comparacin funciona de forma idntica a la mostrada,
haciendo uso de la exclusin lgica para comparaciones de igualdad y la resta, con
los oportunos intercambios de operandos en el resto de comparaciones. Vara, sin
embargo, la comprobacin nal, ya que para valores con signo comprueba el bit
ms alto del resultado pero no es posible hacer lo mismo con operandos sin signo.
Sin embargo, en los valores sin signo no es factible comprobar el signo del resul-
tado: hay que emplear el acarreo. En la resta si el valor del minuendo es mayor o
igual que el sustraendo obtenemos acarreo y en caso contrario no. Por lo tanto, en
un menor-que sin signo, slo tenemos que comprobar que no haya acarreo. Esto in-
dica que el primer valor es menor que el segundo. Para el caso de mayor-o-igual-que
miramos que s se produzca acarreo en la resta. Adems, como antes, no usaremos
para un mayor-que la comparacin directa, ya que nos obligara a ver que existe
acarreo pero adems que el resultado no es cero. En su lugar intercambiamos los
operandos y usamos el menor-que.
Hemos analizado el principal salto condicional y su traduccin, por parte de
GCC, a comparacin y salto. Las dems estructuras de control usan mecanismos si-
milares a los que acabamos de ver, con distinta preparacin pero idnticos procesos
de comparacin.
Compilador de C para el microcontrolador Microchip PIC18F4550 124
7.6.2. switch
La estructura switch basa su funcionamiento en if condicionales de igualdad. La
expresin a evaluar con switch se va comparando, por orden de aparicin, con el
valor de cada caso (case) y, en caso de cumplirse la igualdad se ejecuta el cuerpo
correspondiente a dicho caso hasta llegar a una sentencia break o, en su defecto,
llegar al nal del cuerpo de la estructura switch. Si la expresin no se corresponde
con ningn case, no se ejecutar cdigo alguno, a menos que exista un caso por
defecto (default).
La estructura general es la siguiente:
switch (x)
{
case a:
... /
*
Bloque A
*
/
break;
case b:
... /
*
Bloque B
*
/
break;
case c:
... /
*
Bloque C
*
/
break;
case d:
... /
*
Bloque D
*
/
break;
case e:
... /
*
Bloque E
*
/
break;
default:
... /
*
Bloque Default
*
/
break;
}
Su traduccin a ensamblador es:
Compilador de C para el microcontrolador Microchip PIC18F4550 125
movlw low D99 ; x == c?
xorwf F_REG+.16, W
btfss STATUS, Z
goto _L15
movlw high D99
xorwf F_REG+.16+1, W
_L15
btfsc STATUS, Z
goto _L10
movf F_REG+.16, W ; x < c?
sublw low D99
movf F_REG+.16+1, W
btfss STATUS, C
incfsz F_REG+.16+1, W
sublw high D99
andlw 0x80
btfss STATUS, Z
goto _L8
movlw low D97 ; x == a?
xorwf F_REG+.16, W
btfss STATUS, Z
goto _L16
movlw high D97
xorwf F_REG+.16+1, W
_L16
btfsc STATUS, Z
goto _L11
movlw low D98 ; x == b?
xorwf F_REG+.16, W
btfss STATUS, Z
goto _L17
movlw high D98
xorwf F_REG+.16+1, W
_L17
btfsc STATUS, Z
Compilador de C para el microcontrolador Microchip PIC18F4550 126
goto _L12
; En otro caso -> default
goto _L2
_L8:
movlw low D100 ; x == d?
xorwf F_REG+.16, W
btfss STATUS, Z
goto _L18
movlw high D100
xorwf F_REG+.16+1, W
_L18
btfsc STATUS, Z
goto _L13
movlw low D101 ; x == e?
xorwf F_REG+.16, W
btfss STATUS, Z
goto _L19
movlw high D101
xorwf F_REG+.16+1, W
_L19
btfsc STATUS, Z
goto _L14
_L2:
... ; Bloque Default
goto _L1
_L10:
... ; Bloque C
goto _L1
_L11:
... ; Bloque A
goto _L1
_L12:
... ; Bloque B
goto _L1
_L13:
... ; Bloque D
Compilador de C para el microcontrolador Microchip PIC18F4550 127
goto _L1
_L14:
... ; Bloque E
nop
_L1:
El cdigo resultante es notablemente ms extenso que el de un if simple, debido
a que no deja de ser una sucesin de estructuras if-else. A pesar de su tamao, es
sencillo de entender; ms an con los comentarios aadidos para el ejemplo.
GCC no efecta las comparaciones en el orden en que aparecen los case en el
cdigo C original, sino que opta por ordenar los valores especicados en tiempo de
compilacin, y genera el cdigo ensamblador correspondiente a una bsqueda binaria.
Comenzando con la primera comparacin, si la expresin, cuyo valor est pre-
viamente alojado en los registros 16 y 17, es igual que el valor comparado (99 en
nuestro ejemplo, correspondiente al carcter c en cdigo ASCII), ejecuta el salto a
la etiqueta L10, que marca el comienzo del cdigo ensamblador correspondiente al
bloque C. En caso de no satisfacerse la igualdad, comprueba si la expresin es me-
nor o mayor; y, segn sea, desciende por una rama del rbol de bsqueda o por la
otra. Este proceso se repite sucesivamente hasta llegar a alguna hoja del rbol de
bsqueda binaria y, en caso de llegar a una hoja y que sta no corresponda al va-
lor de la expresin evaluada, ejecuta el cdigo correspondiente al bloque default y
concluye con un salto incondicional a la etiqueta L1, la cual corresponde a la prime-
ra instruccin fuera de la estructura switch y da por nalizada la ejecucin de esta
estructura de seleccin.
7.7. Estructuras iterativas o bucles
Los bucles, junto con las estructuras condicionales y la ejecucin secuencial, son
las bases de la programacin estructurada. Con ellos es posible implementar cual-
quier algoritmo existente, otorgndonos la potencia necesaria para enfrentar todos
los problemas resolubles.
7.7.1. while
La estructura while ejecuta su cuerpo una y otra vez mientras la condicin de su
cabecera sea verdadera.
Veamos un ejemplo de cmo se traduce a ensamblador un bucle while:
while (x < 10)
Compilador de C para el microcontrolador Microchip PIC18F4550 128
{
...
}
El cdigo mostrado arriba, una vez ms, no muestra el contenido del cuerpo del
bucle ya que es irrelevante para nuestra explicacin. En su lugar hemos optado por
representarlo mediante puntos suspensivos cuya traduccin al ensamblador, igual
que con los ejemplos anteriores, no tiene traduccin, por lo que aparecern tal cual.
goto _L2
_L3:
...
_L2:
movf F_REG+.16, W
sublw low D9
movf F_REG+.16+1, W
btfss STATUS, C
incfsz F_REG+.16+1, W
sublw high D9
andlw 0x80
btfsc STATUS, Z
goto _L3
7.7.2. do-while
En la variante do-while, al contrario que con el while, la comprobacin se efecta
al nal de la primera ejecucin del cuerpo, por lo que ste siempre se ejecuta al
menos una vez. No es difcil deducir que eliminando la primera instruccin goto de
la compilacin anterior obtendramos el comportamiento de una estructura do-while
y as lo considera tambin GCC.
do {
...
} while (x<10);
Queda en su conversin a ensamblador como:
_L2:
...
Compilador de C para el microcontrolador Microchip PIC18F4550 129
movf F_REG+.16, W
sublw low D9
movf F_REG+.16+1, W
btfss STATUS, C
incfsz F_REG+.16+1, W
sublw high D9
andlw 0x80
btfsc STATUS, Z
goto _L2
7.7.3. for
La estructura for, adems de la condicin de ejecucin del cuerpo, incluye un
bloque de inicializacin, que se ejecuta una sola vez antes de comenzar el bucle y
de hacer la primera comprobacin de la condicin, y un bloque de actualizacin,
la cual se ejecuta al nal de cada iteracin, tambin justo antes de comprobar la
condicin de iteracin.
Vemos cmo traduce nuestro port de GCC un bucle for:
for (i=0; i<10; i++)
{
...
}
El cdigo que genera GCC es el siguiente:
; Inicializacin
movff Frame_pointer+1, FSR0H
movff Frame_pointer, FSR0L
clrf INDF0
infsnz FSR0L, F
incf FSR0H, F
clrf INDF0
goto _L2
_L3:
... ; Cuerpo del bucle
; Actualizacin
Compilador de C para el microcontrolador Microchip PIC18F4550 130
movff Frame_pointer+1, FSR0H
movff Frame_pointer, FSR0L
movff INDF0, F_REG+.16
infsnz FSR0L, F
incf FSR0H, F
movff INDF0, F_REG+.16+1
movlw low D1
addwf F_REG+.16, F
movlw high D1
addwfc F_REG+.16+1, F
movff Frame_pointer+1, FSR0H
movff Frame_pointer, FSR0L
movff F_REG+.16, INDF0
infsnz FSR0L, F
incf FSR0H, F
movff F_REG+.16+1, INDF0
_L2:
; Condicin
movff Frame_pointer+1, FSR0H
movff Frame_pointer, FSR0L
movff INDF0, F_REG+.16
infsnz FSR0L, F
incf FSR0H, F
movff INDF0, F_REG+.16+1
movf F_REG+.16,W
sublw low D9
movf F_REG+.16+1,W
btfss STATUS,C
incfsz F_REG+.16+1,W
sublw high D9
andlw 0x80
btfsc STATUS,Z
goto _L3
Como hicimos con la estructura switch, hemos includo unos mnimos comenta-
rios en el cdigo ensamblador para que resulte ms sencillo de comprender.
Compilador de C para el microcontrolador Microchip PIC18F4550 131
El primer bloque es el que realiza la sentencia de inicializacin, que en este ejem-
plo se limita a poner a cero una variable de dos bytes. A continuacin salta hasta la
posicin etiquetada como L2, donde comienza el bloque que efecta la comproba-
cin de la condicin de iteracin. En caso de resultar verdadera esta comprobacin,
ejecuta el cuerpo del bucle (etiqueta L3) y, acto seguido, los tres bloques correspon-
dientes al apartado de actualizacin de la cabecera del for. Una vez hecho esto, entra
de nuevo en el bloque etiquetado con L2 y el proceso se repite hasta que la condicin
de iteracin es falsa y el bucle termina.
7.8. Funciones
El empleo de funciones permite escribir programas ms legibles, mejor estruc-
turados y ms fciles de depurar si se emplean debidamente. Asimismo, el uso de
llamadas a funcin permite reutilizar cdigo sin necesidad de duplicarlo, ahorran-
do notablemente el consumo de memoria de programa. Por todo esto, resulta muy
interesante disponer de esta caracterstica a la hora de trabajar con microcontrola-
dores, donde la memoria es escasa.
En nuestra implementacin distinguimos dos tipos de funciones, las denomina-
das genricas y la funcin principal main, a la que tratamos de un modo diferente
por ser la funcin que engloba a todo el programa. El motivo de tratar a la funcin
main de manera diferente al resto de funciones se debe a que es la funcin inicial
del programa: por encima de ella no hay ninguna otra funcin, as que las tareas
previas y pstumas de sta son diferentes.
7.8.1. Genricas
Veamos la siguiente funcin:
int funcion (int x)
{
int y = 10;
if (y != x)
x++;
return x;
}
Compilador de C para el microcontrolador Microchip PIC18F4550 132
Toda funcin tiene un prlogo que podemos dividir en dos partes: salvar los
registros que deban conservar su valor tras la ejecucin de la funcin, y reservar
un espacio en la memoria de datos donde albergar las variables que componen el
marco de la funcin llamada. El cdigo del prlogo correspondiente a la funcin de
ejemplo es el siguiente:
global _funcion
_funcion_code CODE
_funcion:
; Salvar en pila el frame pointer
; de la funcin superior.
movff Stack_pointer+1, FSR0H
movff Stack_pointer, FSR0L
movff Frame_pointer, INDF0
incf FSR0L, F
btfsc STATUS, C
incf FSR0H, F
movff Frame_pointer+1, INDF0
; Reservar un nuevo marco para
; la funcin actual.
movlw .4
movwf Frame_pointer+1
call _allocate_frame_8_banks
; Actualizar el puntero a la cima de la pila.
movlw D2
addwf Stack_pointer, F
btfsc STATUS, C
incf Stack_pointer+1, F
Las lneas de 1 a 3 indican el nombre de la funcin y la hacen accesible desde
cualquier punto del programa. Una vez hecho esto, el siguiente paso ser guardar
el contenido de todos los registros que se utilizan en la funcin y que, por tanto,
debern permanecer con su valor tras la misma. Puesto que GCC ha determinado
que no tendr que utilizar ninguno de los registros fsicos marcados con 0 en la
macro CALL_USED_REGITERS, no es necesario incluir el cdigo para guardar ni,
posteriormente, restaurar los valores de estos registros.
Compilador de C para el microcontrolador Microchip PIC18F4550 133
En el segundo bloque de este cdigo se efecta la reserva de memoria de da-
tos necesaria para la ejecucin de esta funcin. Para ello, efecta una llamada a la
funcin _allocate_frame_8_banks, indicando en Frame_pointer+1 el nmero de bytes a
reservar. Al volver de esta funcin, el frame pointer contendr la direccin del marco
de funcin reservado.
El tercer bloque actualiza el apuntador de cima de pila segn los registros que
se hayan salvado en el primer bloque. En este ejemplo, el stack pointer slo crece en
dos unidades, ya que tan slo se salv el frame pointer, 2 bytes.
Despus de ejecutar el cuerpo de una funcin, es necesario restaurar los valores
de los registros que se salvaron en la pila; as como restaurar el frame pointer para
que apunte de nuevo al marco de la funcin a la que retornamos. El cdigo del
eplogo de la funcin que hemos puesto como ejemplo se muestra a continuacin:
; Liberar memoria del marco de funcin.
movlw .4
call _free_frame_8_banks
; Restaurar registros salvados en pila.
dcfsnz Stack_pointer, F
decf Stack_pointer+1, F
movff Stack_pointer+1, FSR0H
movff Stack_pointer, FSR0L
movff INDF0, Frame_pointer+1
dcfsnz FSR0L, F
decf FSR0H, F
movff INDF0, Frame_pointer
movlw low D2
subwf Stack_pointer, F
bnc _L5
movlw high D2
subwfb Stack_pointer+1, F
_L5
retlw 0
En el cdigo anterior, el primer bloque se encarga de liberar los 4 bytes reserva-
dos para el marco de la funcin llamada. Posteriormente, en el segundo bloque se
restauran los valores de los registros que se salvaron en la pila, en orden inverso a
como se apilaron en el prlogo de la funcin.
Compilador de C para el microcontrolador Microchip PIC18F4550 134
No analizaremos ningn cdigo para pasar parmetros o devolverlos, pero co-
mo vimos en la especicacin, el cdigo ensamblador que genere nuestro back-end
hace el paso de parmetros copiando su valor en registros si se trata de datos de tipo
simple; o copiando un puntero a memoria para el caso de tipos de dato complejos.
Lo mismo ocurre con el retorno de valores, por lo que a nivel de cdigo no existe
diferencia entre una funcin con parmetros y/o valores de retorno y otra que no
los tenga.
7.8.2. main
Veamos una funcin main:
void main (void)
{
int x = 10, y = 2;
while (y > 0)
{
x = x
*
y;
y--;
}
}
La primera idea que nos puede venir a la cabeza es que sucede lo mismo que
con las funciones genricas; pero en este caso existen varias diferencias esenciales
que descubrimos al analizar el cdigo resultante de compilar esta funcin main de
ejemplo.
global main
_main CODE
main:
; Inicializar puntero a la cima
; de la pila
BANKSEL F_REG
movlw low stackdata
movwf Stack_pointer
movlw high stackdata
movwf Stack_pointer+1
Compilador de C para el microcontrolador Microchip PIC18F4550 135
; Reservar un nuevo marco para
; la funcin actual.
movlw .4
movwf Frame_pointer+1
call _allocate_frame_8_banks
Observamos que no hay ningn bloque de cdigo que se encargue de guardar
en pila ningn registro. Esto es correcto debido a que main es la primera funcin
que ejecuta nuestro programa, por lo que no hay que salvar el marco de la funcin
que llame a sta. En su lugar encontramos, justo antes de la llamada a la funcin de
reserva de marco, un bloque de cdigo que inicializa el stack pointer para que apunte
a la primera posicin de memoria correspondiente al segmento designado para la
pila de datos.
La funcin main no devuelve ningn valor debido a que el programa no se eje-
cuta sobre un sistema operativo que reciba el resultado, as que el eplogo de esta
funcin es un simple bucle innito.
_epimain:
goto _epimain
7.9. Archivos
El ltimo punto de la estructura de un programa en C son los archivos. Un pro-
grama mediano C suele estar compuesto por diversos archivos de cdigo fuente,
los cuales se compilan por separado; y, nalmente, se enlazan los cdigos objeto
resultantes de compilar cada uno de ellos.
Distinguiremos entre dos tipos de archivo. El archivo que denominamos princi-
pal es aquel que alberga la denicin de la funcin main. Por otro lado, cualquier
archivo de cdigo que forme parte del programa y no se trate del principal, lo de-
nominamos genrico.
7.9.1. Genricos
Dejando a un lado el cdigo correspondiente al cuerpo de las funciones, nos
queda por analizar de los archivos genricos su cabecera y su terminacin.
En la cabecera, la lnea fundamental es la que incluye el archivo de deniciones
para el dispositivo que estamos programando, en nuestro caso, p18f4550.inc.
;START
Compilador de C para el microcontrolador Microchip PIC18F4550 136
#include "p18f4550.inc"
La terminacin de un archivo genrico est compuesto por las declaraciones
de las variables y funciones externas utilizadas en el archivo actual. En el caso de
este ejemplo, tan slo declara las funciones de reserva y liberacin de marco, y las
variables internas que utiliza GCC para gestionar la memoria del PIC.
extern _allocate_frame_8_banks
extern _free_frame_8_banks
extern stackdata
extern Mem_entr
extern F_REG
extern Frame_pointer
extern Stack_pointer
extern Arg_pointer
7.9.2. Principal
El archivo principal es bastante ms complejo que los archivos genricos. Su
cabecera es similar a la genrica, pero el nal del archivo incluye el cdigo de de-
nicin e inicializacin de los distintos segmentos de memoria, incluidos el segmento
asignado a registros y el asignado a pila de datos.
Lo primero que encontramos en el nal del archivo principal es la declaracin
de las funciones de biblioteca llamadas desde las funciones de este archivo.
extern _mulhisi
extern _allocate_frame_8_banks
A continuacin encontramos una etiqueta de cdigo absoluto, ubicada en la po-
sicin 0x000000 de la memoria de programa, que corresponde con el vector de re-
set del microcontrolador. Como ya explicamos unos captulos atrs, la primera ins-
truccin que ejecuta el PIC al inicializarse es la que est alojada en la posicin de
memoria 0x000000. Por ello, esta etiqueta del cdigo es fundamental para el funcio-
namiento del programa generado. Nuestro reset simplemente salta a la rutina _init,
que efecta la inicializacin de variables, si las hubiera, y, acto seguido, salta a la
primera instruccin de la funcin main.
_reset CODE 0x0
Compilador de C para el microcontrolador Microchip PIC18F4550 137
goto _init
_initcode CODE
_init clrf INTCON
call _initialize
goto _main
Despus de estas instrucciones en ensamblador encontramos ms directivas que
indican a gpasm y gplink cmo deben manejar la memoria. Por un lado, declaramos
una regin del banco de acceso del PIC que corresponde a las posiciones de me-
moria designadas para que GCC las consideres registros, tanto los 32 registros de
propsito general como los tres registros de propsito especco y tamao distinto
al de los genricos. Por el otro, declaramos otra regin de memoria, no importa de
qu banco, que ser la pila de datos de nuestro programa compilado.
v_reg UDATA_ACS
F_REG res .32
Frame_pointer res 2
Stack_pointer res 2
Arg_pointer res 2
global F_REG
global Frame_pointer
global Stack_pointer
global Arg_pointer
seg_stackDATA UDATA_ACS
stackdata res .16
global stackdata
El cdigo del archivo principal termina con la reserva de toda la memoria de
los bancos que existen en el PIC; reserva e inicializa los dos primeros bytes de cada
banco, con el propsito de almacenar en ellos el tamao total de cada banco y el
espacio utilizado hasta el momento por nuestro programa. Adems, reserva toda
la memoria restante de cada banco para evitar que gpasm o gplink intereran con
la gestin de memoria. Nuestro programa, tras la compilacin, incorpora toda la
lgica necesaria para gestionar la memoria de datos y, de este modo, tenemos la
certeza de que toda la memoria est disponible y bajo nuestro control.
bank1DATA IDATA 0x60
Compilador de C para el microcontrolador Microchip PIC18F4550 138
b1_used db D0
b1_total db D158
bank1UDATA UDATA 0x62
b1_data res .158
global b1_used
global b1_total
global b1_data
bank2DATA IDATA 0x100
b2_used db D0
b2_total db D254
bank2UDATA UDATA 0x102
b2_data res .254
global b2_used
global b2_total
global b2_data
bank3DATA IDATA 0x200
b3_used db D0
b3_total db D254
bank3UDATA UDATA 0x202
b3_data res .254
global b3_used
global b3_total
global b3_data
bank4DATA IDATA 0x300
b4_used db D0
b4_total db D254
bank4UDATA UDATA 0x302
b4_data res .254
global b4_used
global b4_total
global b4_data
Compilador de C para el microcontrolador Microchip PIC18F4550 139
bank5DATA IDATA 0x400
b5_used db D0
b5_total db D254
bank5UDATA UDATA 0x402
b5_data res .254
global b5_used
global b5_total
global b5_data
bank6DATA IDATA 0x500
b6_used db D0
b6_total db D254
bank6UDATA UDATA 0x502
b6_data res .254
global b6_used
global b6_total
global b6_data
bank7DATA IDATA 0x600
b7_used db D0
b7_total db D254
bank7UDATA UDATA 0x602
b7_data res .254
global b7_used
global b7_total
global b7_data
bank8DATA IDATA 0x700
b8_used db D0
b8_total db D254
bank8UDATA UDATA 0x702
b8_data res .254
global b8_used
Compilador de C para el microcontrolador Microchip PIC18F4550 140
global b8_total
global b8_data
Como conclusin a este captulo debemos recordar que, pese a que hemos visto
de forma genrica todos los cdigos ensamblador generados a partir de las ins-
trucciones atmicas de C, GCC, desde el primer nivel de optimizacin, alterar la
estructura del programa para reducir su coste de ejecucin. Como veremos en el
captulo 8, la autntica potencia de GCC se descubre al hacer uso de las diversas
opciones de optimizacin.
Captulo 8
Optimizando cdigo con GCC
8.1. Tipos de optimizacin
GCC ofrece mltiples opciones de optimizacin que modican la manera de ge-
nerar cdigo del compilador. Adems, ofrece cuatro niveles de optimizacin, los
cuales no son ms que agrupaciones de las diversas opciones de optimizacin indi-
viduales.
Nivel 0
El primer nivel es no optimizar. En esta modalidad, el principal objetivo del
compilador es reducir el coste de compilacin y conseguir cdigo sin modicar, tal
cual esperbamos, para poder usarlo para depuracin. Las sentencias son compila-
das independientemente, con lo que podemos parar la ejecucin en un depurador
entre dos sentencias cualesquiera y acceder a las variables directamente.
Es obvio que el cdigo que obtenemos sin ninguna optimizacin es pesado e
ineciente. Las sentencias que nunca sean ejecutadas tambin se traducirn en c-
digo ensamblador y toda variable ser leda de memoria, usada segn la operacin
y devuelta a memoria. Aunque volvamos a usar una variable, sta no ser leda
desde un registro, sino que se repite el proceso completo de acceso a memoria. Por
tanto el nivel de optimizacin cero (nivel por defecto) no es aconsejable si no se va
a depurar el cdigo. Es posible indicar este nivel de manera explcita con la opcin
-O0.
Con los otros tres niveles activamos, de manera incremental, distintas opciones
de compilacin que indican a GCC las estrategias que debe aplicar para generar
cdigo. Todas las optimizaciones se consiguen a costa de incrementar el tiempo de
compilacin. Adems, un cdigo optimizado se vuelve muy difcil de depurar.
141
Compilador de C para el microcontrolador Microchip PIC18F4550 142
Nivel 1
El nivel siguiente de optimizacin es el nivel 1, que lo especicamos a GCC con
el parmetro -O1 o smplemente -O. En este nivel, el optimizador intenta llegar a
un equilibrio entre la reduccin del tamao del cdigo, el tiempo de ejecucin del
cdigo ensamblador generado y el tiempo de compilacin del programa. Con este
nivel se evitan los modicadores que aumentan demasiado el tiempo de compila-
cin y los que apenas suponen una reduccin del tiempo de ejecucin del programa
compilado respecto a su versin sin optimizar.
Los modicadores activados en este nivel de optimizacin son:
-fauto-inc-dec: Combina incrementos o decrementos de direcciones con accesos
a memoria.
-fcompare-elim: Evita las operaciones de comparacin cuando sea posible.
fcprop-registers: Tras la pasada de localizar registros, se efecta la pasada de
propagacin de copia, que intenta reducir dependencias y en el mejor caso
evitar la copia de registros.
-fdce: Realiza una eliminacin de cdigo muerto o no ejecutado sobre el RTL.
-fdefer-pop: Hace que los argumentos de una funcin no sean sacados inmedia-
tamente de la pila una vez acabada la misma, sino que queden en la pila para
funciones posteriores y saque todos los argumentos de una sola vez.
-fdelayed-branch: Intenta reorganizar las instrucciones para poder aprovechar
los huecos introducidos al retrasar saltos.
-fguess-branch-probability: Realiza una conjetura ante un salto para elegir la ra-
ma que tiene ms probabilidad de ser ejecutada y darle ms prioridad. Para
elegir la rama, usa diversos mtodos heursticos basados en la informacin
recogida de la funcin.
-f-conversion: Intenta transformar los saltos condicionales en saltos equiva-
lentes pero sin ramas.
-f-conversion2: Usa ejecucin condicional si est disponible a nivel de hardware
para reducir o eliminar los saltos.
-pa-pure-const: Descubre qu funciones devuelven siempre un valor constan-
te y reemplaza la llamada por dicho valor.
-pa-prole: Efecta propagacin de perles entre procedimientos.
Compilador de C para el microcontrolador Microchip PIC18F4550 143
-pa-reference: Descubre qu variables globales son internas al archivo que se
compila.
-fmerge-constants: Intenta mezclar constantes idnticas a travs de las unidades
de compilacin.
-fomit-frame-pointer: No guarda el puntero a frame en registros para funciones
que no lo necesiten.
-fsplit-wide-types: Divide los tipos grandes entre varios registros independien-
tes.
-ftree-builtin-call-dce: Elimina cdigo muerto de llamadas a funciones internas
del compilador.
-ftree-ccp: Realiza una propagacin de las constantes condicionales en el rbol
RTX.
-ftree-ch: Copia la cabeza de los bucles a travs del rbol de compilacin. In-
crementa la efectividad de las optimizaciones sobre el ujo de control pero
incrementa el tamao del cdigo.
-ftree-copyrename: Intenta renombrar las variables temporales del compilador
a otras variables copiando las localizaciones. Resulta, de forma general, un
cdigo con los nombres de las variables ms cercanos a los originales.
-ftree-dce: Realiza una eliminacin de cdigo muerto o no ejecutado sobre el
rbol de RTL.
-ftree-dominator-opts: Efecta una variedad de limpiezas simples sobre enteros,
como la propagacin de constantes, eliminacin de redundancia o simplica-
cin de expresiones, basadas en la rama dominante del rbol.
-ftree-dse: Realiza eliminacin de almacenamiento no til.
-ftree-forwprop: Efecta propagacin hacia delante en rboles.
-ftree-fre: Realiza eliminacin de redundancia de forma completa. Se diferencia
de -ftree-pre en que slo considera expresiones que estn en todas las ramas.
-ftree-sra: Reemplaza escalares por tipos agregados, reemplazando las referen-
cias a estructuras con escalares para prevenir pasos de estructuras a memoria
demasiado pronto.
-ftree-ter: Reemplaza expresiones temporales durante la fase de paso de SSA a
normal.
Compilador de C para el microcontrolador Microchip PIC18F4550 144
Nivel 2
El segundo nivel de optimizacin lo activamos con el parmetro -O2. En este
nivel aumentamos la complejidad de compilacin y el rendimiento del cdigo obte-
nido. En este nivel, GCC utiliza todos los modicadores de optimizacion salvo los
que desenrollan bucles y empotran funciones en lnea.
Este nivel incluye todos los modicadores del nivel 1 y, adems, los siguientes:
-fthread-jumps: Optimiza los saltos en los que una de las ramas lleva a otra
comprobacin que es un subconjunto de la anterior. En este caso salta directa-
mente al punto de destino del segundo salto o el punto justo despus de ese
bloque.
-falign-functions: Alinea el comienzo de las funciones con direcciones de me-
moria que sean potencia de dos.
-falign-jumps: Alinea los destinos de los saltos con direcciones de memoria que
sean potencia de dos.
-falign-loops: Alinea el comienzo de los bucles con direcciones de memoria que
sean potencia de dos.
-falign-labels: Alinea las etiquetas de los bloques de cdigo con direcciones de
memoria que sean potencia de dos.
-fcaller-saves: Activa la localizacin de valores en registros que sean utilizados
en una llamada a funcin, evitando con ello su almacenamiento y recupera-
cin en el cuerpo de la funcin.
-fcrossjumping: Realiza una transformacin de saltos entrecruzados, unican-
do el cdigo equivalente y ahorrando espacio.
-fcse-follow-jumps: Optimiza los saltos que sea posible eliminando subexpre-
siones comunes.
-fcse-skip-blocks: Hace que la fase de CSE optimice los saltos con bloques con-
dicionales.
-fdelete-null-pointer-checks: Hace un anlisis global del ujo de datos para iden-
ticar y eliminar los chequeos de apuntadores nulos.
-fdevirtualize: Intenta convertir las llamadas a funciones virtuales en llamadas
directas a funcin.
Compilador de C para el microcontrolador Microchip PIC18F4550 145
-fexpensive-optimizations: Efecta un gran nmero de pequeas optimizaciones
que son relativamente costosas en tiempo de compilacin.
-fgcse: Ejecuta una etapa de eliminacin de subexpresiones globales.
-fgcse-lm: En la fase de eliminacin de subexpresiones globales, mueve las lec-
turas de memoria fuera de los bucles, reemplazndolas en su interior por co-
pias.
-nline-small-functions: Sustituye llamadas a funcin por el cuerpo de las fun-
ciones llamadas cuando ste es ms pequeo que el cdigo de llamada a dicha
funcin.
-ndirect-inlining: Empotra tambin las llamadas indirectas que sean descu-
biertas en tiempo de compilacin.
-foptimize-sibling-calls: Optimiza las llamadas recursivas.
-partial-inlining: Empotra parte de las funciones llamadas.
-fpeephole2: Activa las optimizaciones peephole de la especicacin de la arqui-
tectura.
-fregmove: Intenta reasignar los nmeros de registros utilizados por las instruc-
ciones de movimiento, para optimizar la cantidad de registros utilizados.
-freorder-blocks: Reordena los bloques de cdigo para reducir el nmero de sal-
tos tomados.
-freorder-functions: Reordena las funciones en el cdigo para mejorar la locali-
zacin del cdigo en memoria de programa.
-frerun-cse-after-loop: Repite la eliminacin de subexpresiones comunes des-
pus de la optimizacin de bucles.
-fsched-interblock: Planica la secuencia de instrucciones entre bloques.
-fsched-spec: Permite especular con la ejecucin de instrucciones que no sean
de carga de registros.
-fschedule-insns: Intenta reordenar las instrucciones para eliminar los retrasos
en la ejecucin de algunas instrucciones debidos a que los datos que necesitan
no estn disponibles en ese momento.
Compilador de C para el microcontrolador Microchip PIC18F4550 146
-fschedule-insns2: Similar a -fschedule-insn, pero con una pasada adicional del
planicador de instrucciones tras la asignacin de registros.
-fstrict-aliasing: Permite al compilador aplicar las reglas ms restrictivas para
localizacin en memoria.
-fstrict-overow: Permite al compilador aplicar las reglas ms estrictas para
desbordamiento de valores con signo.
-ftree-switch-conversion: Convierte las inicializaciones simples en una estructu-
ra switch en inicializaciones a partir de un vector de valores.
-ftree-pre: Realiza una eliminacin de redundancias parcial sobre el rbol RTL.
-ftree-vrp: Efecta la propagacin de rangos de valores sobre el rbol RTL.
Nivel 3
El nivel mximo de optimizacin es el 3, y se activa con el parmetro -O3 en la
lnea de comandos. Activa todas las optimizaciones del nivel 2 y, adems, -nline-
functions, -funswitch-loops, -fpredictive-commoning, -fgcse-after-reload, -ftree-vectorize y
-pa-cp-clone. Estas estn orientadas a linealizar los bucles y producir funciones en
lnea, con lo que acabamos con la sobrecarga de las llamadas a funcin.
-nline-functions: Integra todas las funciones simples en las llamadas.
-funswitch-loops: Mueve las ramas de un bucle con condiciones invariantes fue-
ra del bucle.
-fpredictive-commoning: Efecta una optimizacin basada en la prediccin de
operaciones repetidas en cada iteracin de un bucle.
-fgcse-after-reload: Realiza una pasada de eliminacin de cargas de memoria
redundantes tras la pasada de relocalizacin de registros.
-ftree-vectorize: Efecta optimizacin de bucles en el rbol RTL.
-pa-cp-clone: Hace clones de algunas funciones para mejorar la propagacin
de constantes entre procedimientos.
Nivel s
Adems de los niveles numerados, GCC ofrece un conjunto de optimizaciones
orientadas a reducir el tamao del cdigo generado, en lugar del tiempo de ejecu-
cin del programa. Estas opmizaciones se activan con el parmetro -Os.
Captulo 9
Conclusiones
A lo largo de este proyecto hemos desarrollado un compilador de C para el PIC
18F4550, obteniendo como resultado un compilador de C plenamente funcional.
Este compilador desarrollado tiene pleno soporte a todas las caractersticas del len-
guaje, incluyendo matrices, punteros, y llamadas a funciones sin restricciones arti-
ciales.
Hemos optado por trabajar sobre la GCC toolchain, y nos hemos integrado de
forma adecuada en ella segn la especicacin de arquitectura; es decir, solamente
hemos modicado la parte que tenamos que soportar, el backend, ya que es la parte
donde en dicha arquitectura se denen los portings de GCC a arquitecturas concre-
tas. Esto ha supuesto un sobreesfuerzo; ya que nos ha obligado a soportar la GCC
toolchain por completo, sin trampas ni atajos. Teniendo en cuenta que es comn en
los compiladores para microcontroladores el no soportar plenamente determinadas
caractersticas del lenguaje, para facilitar el trabajo de desarrollo del compilador, el
resultado de este proyecto queda a la altura de los mejores compiladores de C para
microcontroladores.
Gracias, sin embargo, a nuestro planteamiento, soportamos C por completo, so-
portamos las posibilidades de optimizacin de GCC, y abrimos la puerta a soportar
los dems lenguajes que admite la GCC toolchain, haciendo apenas una serie de
desarrollos acotados y denidos.
Adems, hemos sido muy cuidadosos en este proyecto de trabajar con la ltima
versin de GCC toolchain, as como de seguir las guias de estilo y de modicar solo
lo que en la GCC toolchain se espera que modiques con objeto de una potencial
inclusin futura en la lnea principal de GCC; un asunto que actualmente estamos
tratando con el equipo de desarrollo de GCC.
Este proyecto abre nuevas lneas futuras de inters; entra las cuales hay dos es-
pecialmente destacadas: el desarrollo de bibliotecas especcas para dispositivos
del PIC 18F4550, y ampliar el trabajo para soportar el resto de lenguajes de la GCC
147
Compilador de C para el microcontrolador Microchip PIC18F4550 148
toolchain. Las caractersticas especcas del software libre, el sobreesfuerzo para que
el cdigo cumpla las caractersticas necesarias para ser integrado en la lnea prin-
cipal de GCC y el hecho de que vamos a liberar el cdigo fuente garantizan que el
desarrollo de este proyecto ser mantenido, y resultar de utilidad a otras personas.
Bibliografa
[1] A. V. Aho y col. Compiladores: principios, tcnicas y herramientas. Addison-Wesley,
1998.
[2] F. J. Sanchs Llorca, C. Galn Pascual. Compiladores: teora y construccin. Para-
ninfo, 1988.
[3] Jan Hubicka. Porting GCC to the AMD64 architecture. URL: http://www.uc
w.cz/~hubicka/papers/amd64.pdf.
[4] Microchip. Pgina principal de MicroChip. 2011. URL: http://www.microch
ip.com.
[5] Hans-Peter Nilsson. Porting GCC for dunces. URL: ftp://ftp.axis.se/pu
b/users/hp/pgccfd/pgccfd.pdf.
[6] David Santo Orcero. Arquitectura interna de GCC toolchain. En: Mundo
Linux 78 (2005), pgs. 58-63.
[7] David Santo Orcero. Generacin de cdigo ensamblador con la GCC tool-
chain. En: Mundo Linux 79 (2005), pgs. 56-61.
[8] Pedro Jos Ramirez Gutierrez, dir. David Santo Orcero. Compilador de C para
el microcontrolador Microchip PIC16F877. 2007.
[9] GCC Team. GCC Internals. 2011. URL: http://gcc.gnu.org/onlinedoc
s/gccint.
[10] GCC Team. Pgina principal de GCC. 2011. URL: http://gcc.gnu.org.
149
Apndice A
PIC18-GCC
A.1. Compilando GCC
Para poder compilar GCC con la arquitectura PIC18 como target es necesario dis-
poner del cdigo fuente de GCC includo en el CD que acompaa esta memoria. La
versin utilizada es la futura 4.7, an no publicada, por lo que el cdigo adjunto se
trata de un snapshot (copia instantnea) del repositorio ocial de desarrollo, tomada
el da 29 de septiembre de 2011. Debido a que se trata de una cdigo en desarrollo
activo, no se puede garantizar que el cdigo especco del PIC18 vaya a funcionar
con una instantnea posterior o anterior a la includa en el CD.
A parte del cdigo fuente de GCC y, obviamente, el parche especco del port
para PIC18, es necesario tener instaladas en el sistema las GPUTILS, ya que son
las que utilizar GCC para ensamblar y enlazar el cdigo ensamblador producido.
La mayora de distribuciones de GNU/Linux ofrecen la posibilidad de instalar las
GPUTILS como paquete precompilado desde sus repositorios de software, por lo
que no explicaremos los pasos correspondientes a la compilacin e instalacin de
GPUTILS.
La manera recomendada de aplicar el parche de este port es haciendo uso del
programa patch. Tan slo hay que cambiar al directorio principal del cdigo fuente
de GCC con el comando cd y, desde ah, ejecutar patch de la siguiente forma:
patch -p0 < ruta/hasta/gcc-pic18.patch
Una vez aplicado el parche sobre el cdigo ocial de GCC, lo siguiente es crear un
directorio aparte donde efectuar la compilacin del compilador sin que se mezclen
los archivos compilados con los de cdigo fuente. En este ejemplo, el directorio lo
creamos dentro del directorio que alberga el cdigo fuente, pero no supone ninguna
diferencia el utilizar cualquier otro.
150
Compilador de C para el microcontrolador Microchip PIC18F4550 151
mkdir gcc-pic18
Desde este directorio invocaremos el script de conguracin y, una vez haya
terminado ste, ejecutaremos el comando make, responsable de compilar todo el
proyecto de manera automatizada.
cd gcc-pic18
../configure --target=pic18
make
Tras la ejecucin (con xito) de make, los ejecutables de nuestro compilador se
hallarn en el subdirectorio gcc-pic18/gcc. No obstante, para evitar problemas de
ruta es ms que recomendable instalarlo en sus directorios de prejo utilizando, de
nuevo, el programa make.
make install
Una vez completados estos pasos, tendremos en nuestro sistema una instalacin
completamente integrada del compilador GCC para PIC18, que podremos invocar
desde cualquier directorio.
A.2. Utilidades
De los diversos programas que componen la GNU Compiler Collection, dos de
ellos son especialmente interesantes para el usuario: pic18-cpp, pic18-gcc.
pic18-cpp es el preprocesador de C. Su misin es procesar las macros incluidas
en el cdigo fuente antes de pasrselo al compilador. Aunque este programa
es de uso transparente para el usuario de GCC, es posible ejecutarlo a mano
y no necesariamente con un cdigo escrito en C como entrada. La mayora de
las veces el usuario lo utilizar de manera transparente a travs del driver de
GCC.
pic18-gcc es el driver (controlador) de los compiladores de GCC para PIC18. Es-
te programa es el que utilizar directamente un usuario habitual de GCC. De
manera genrica, toma como entrada uno o varios archivos de cdigo fuente.
En el caso que nos ocupa, el port para PIC18, slo acepta cdigo C y ensam-
blador del PIC18. Tras identicar en qu lenguaje est escrito cada archivo de
entrada, invoca, si procede, al preprocesador; y, acto seguido, al compilador
en s, cc1. Nuestra versin cc1 da como resultado un archivo en ensamblador
Compilador de C para el microcontrolador Microchip PIC18F4550 152
del PIC18. Tras la traduccin a ensamblador, el driver invoca al programa en-
samblador gpasm con el archivo generado por cc1 como entrada. La salida de
gpasm es un archivo de cdigo objeto especco para el PIC18F4550. El lti-
mo paso que efecta el driver es tomar todos los archivos objeto generados y
enlazarlos mediante el programa gplink. Tras todos estos pasos obtenemos un
archivo de extensin .hex, listo para grabarlo en la memoria ash del micro-
controlador.
A.3. Argumentos
Los argumentos de pic18-gcc nos permiten modicar la operacin normal del
compilador. pic18-gcc requiere, como mnimo, un parmetro que sea un archivo C
de entrada. En el caso de no pasar ningn modicador a pic18-gcc, ste compilar el
archivo C con el mnimo de optimizaciones, y el resultado ser ensamblado y enla-
zado con la biblioteca de funciones matemticas, quedndonos el programa ejecu-
table nal en a.hex. Los diversos parmetros que ofrece pic18-gcc permiten modicar
este funcionamiento, incluyendo diversas optimizaciones, generando un archivo de
salida con un nombre concreto o efectuando slo una de las etapas que componen
la compilacin.
A continuacin encontramos una breve lista de los argumentos ms comunes
de gcc. La lista completa se puede encontrar en la pgina de man sobre gcc o en la
documentacin online que se encuentra en http://gcc.gnu.org.
-E: slo ejecuta el preprocesador. Equivale a invocar directamente a cpp.
-S: slo ejecuta el preprocesador y compilador de C. Equivale a invocar a cpp
y, a continuacin, cc1.
-c: ejecuta todas las etapas salvo la nal de enlazado. En este caso usa cpp, cc1
y gpasm, para obtener el cdigo objeto.
-v: muestra los programas invocados por el driver. De este modo podemos
seguir la pista a los programas invocados y sus parmetros.
-dP: incluye en el cdigo ensamblador indicaciones de las insn que generan el
cdigo.
-o nombre_de_archivo: especica el nombre del archivo de salida.
-On: nivel de optimizacin. GCC ofrece varios niveles generales de optimiza-
cin: 0,1,2,3 y s. En el captulo 8 veremos ms detalles sobre las optimizacio-
nes.
Compilador de C para el microcontrolador Microchip PIC18F4550 153
-M: muestra en la salida estndar los comandos para poder compilar el archi-
vo de entrada desde un Makele.
La forma habitual en la que invocaremos al compilador GCC es con el parmetro
-c, que nos entregar, ante un archivo C de entrada, un archivo ensamblador con
el mismo nombre y cuya extensin ser .s.
A.4. Parmetros de GCC especcos para el PIC18
El back-end para PIC18 admite dos parmetros para modicar su funcionamien-
to. Estos argumentos permiten ajustar el funcionamiento del compilador segn las
necesidades del desarrollador y los requisitos del proyecto que est desarrollando.
-mextra-alloc=n_bytes: Expecica cuantos bytes de memoria deber reservar
GCC de manera adicional. Este parmetro est pensado para programas de
varios archivos de cdigo fuente en los cuales la declaracin de variables glo-
bales no slo se hace en el archivo que alberga la funcin main.
-mstack-size=<tamao>: Especica el tamao en bytes de la pila software. Por
defecto es de 16 bytes.
A.5. Compilando con pic18-gcc
Supongamos que tenemos un archivo codpic.c con el cdigo C del programa
deseado. Para compilarlo basta con pasrselo como argumento a pic18-gcc, pero
una manera ms controlada de hacerlo es la siguiente:
pic-gcc -O3 -mp=18f4550 -o codpic.hex codpic.c
Con esto obtendremos el archivo codpic.hex listo para programarlo directamen-
te en el microcontrolador. Adems obtendremos el listado en ensamblador en cod-
pic.lst con la informacin de enlazado includa, y en codpic.cod podremos encontrar
la informacin extendida lista para utilizar con un simulador o las herramientas
auxiliares de GPUTILS.
Con programas de mayor envergadura lo ms probable es que necesitemos em-
plear varios archivos de cdigo fuente. Para compilar este tipo de proyecto es re-
comendable generar los cdigos objeto primero y despus enlazarlos todos en un
nico archivo hex. Por ejemplo, supongamos un programa codpic complejo, cuyo c-
digo fuente se encuentra repartido en tres archivos diferentes, codpic1.c, codpic2.c y
Compilador de C para el microcontrolador Microchip PIC18F4550 154
codpicmain.c. La secuencia de comandos para compilar este programa sera similar
a la siguiente:
pic18-gcc -O3 -mp=18f4550 codpicuno.c -c
pic18-gcc -O3 -mp=18f4550 codpicdos.c -c
pic18-gcc -O3 -mp=18f4550 codpicmain.c -c
pic18-gcc -o codpic.hex codpicmain.o codpicuno.o codpicdos.o
Las tres primeras llamadas a pic18-gcc se encargan de compilar y ensamblar los
archivos fuente individuales. Con la cuarta llamada al driver, ste identica los ar-
chivos de entrada como cdigo objeto y se limita a llamar al enlazador, el cual gene-
ra el archivo nal codpic.hex. Todo esto es posible automatizarlo mediante un script
de make como el mostrado a continuacin:
MC=18f4550
CC=pic18-gcc
CFLAG=-mp=$(MC)
codpic.hex: codpicmain.o codpicuno.o codpicdos.o
$(CC) -o codpic.hex codpicmain.o codpicuno.o codpicdos.o
codpicmain.o: codpicmain.c
$(CC) $(CFLAG) -O3 -c codpicmain.c
codpicuno.o: codpicuno.c
$(CC) $(CFLAG) -O3 -c codpicuno.c
codpicdos.o: codpicdos.c
$(CC) $(CFLAG) -O3 -c codpicdos.c
Con este makele, bastar invocar al comando make para efectuar la compilacin
del hipottico programa complejo puesto como ejemplo. Adems, make detecta qu
archivos fuente han cambiado desde la ltima vez que se compil el programa nal
y slo compilar los archivos fuente ms recientes, de manera cmoda, eciente y
sin la posibilidad de saltarse ningn paso.
Apndice B
GNU PIC Utilities
B.1. Introduccin
Las GNU PIC Utilities, tambin conocidas como GPUTILS, son una coleccin de
herramientas que nos permiten trabajar con los microcontroladores PIC de Micro-
chip. Incluyen tres herramientas clave en todo proceso de trabajo con un microcon-
trolador: un ensamblador (gpasm), un enlazador (gplink) y una herramienta para
creacin y mantenimiento de bibliotecas (gplib). Todas estas herramientas fueron
creadas con un claro objetivo: proporcionar un sustituto libre (licencia GPL) de las
correspondientes herramientas propietarias de Microchip.
Cuando se programa para un microprocesador, una de las primeras decisiones
que hay que tomar es qu tipo de cdigo utilizar el proyecto. Exsiten dos opciones:
Modo absoluto. En este modo es el desarrollador quien decide qu posiciones
de memoria ocupar cada bloque de cdigo y cada variable de memoria. En
este modo slo se utiliza el lenguaje ensamblador y gpasm, ya que el cdigo
fuente incluye toda la informacin de almacenamiento necesaria. Este modo
es idneo para programar el microprocesador directamente en lenguaje en-
samblador.
Cuando se trabaja en este modo, es habitual organizar el cdigo de manera
que cumpla el principio de localidad espacial, es decir, que las variables co-
munes estn alojadas en posiciones prximas y, en el caso del PIC18, dentro
del mismo banco de memoria. De este modo se evita el uso repetido del cdi-
go para cambiar de banco, haciendo el programa ms fcil de leer y ahorrando
espacio en la memoria de programa.
El principal inconveniente de este modo es que la utilizacin de bibliotecas ex-
ternas resulta ms tediosa, debido a la obligatoriedad de ubicar las funciones
en memoria manualmente.
155
Compilador de C para el microcontrolador Microchip PIC18F4550 156
Modo relocalizable: Las posiciones de memoria utilizadas en este modo son re-
lativas. Es posible dividir el cdigo ensamblador en mdulos separados, los
cuales pueden ensamblarse en archivos objeto diferentes. gpasm es el encarga-
do de crear smbolos para cada referencia a memoria (tanto para datos como
para cdigo). Estos mdulos objeto pueden cargarse en cualquier posicin de
memoria, por lo que ahorra al desarrollador esta responsabilidad. Por otro la-
do, la responsabilidad del enlazador, gplink, es mucho mayor en este modo,
puesto que es l quien se encarga de decidir la posicin nal de los objetos,
resolviendo las referencias a los smbolos creados por en ensamblador. El ar-
chivo producido por gplink ser un nico archivo ejecutable en modo absoluto.
Como primera ventaja de trabajar con el modo relocalizable, vemos que la escri-
tura de ensamblador se vuelve ms fcil, ya que no tenemos que preocuparnos por
las direcciones del cdigo. Podemos crear mdulos objeto compuestos por funcio-
nes relacionadas y agruparlos en bibliotecas, permitiendo de este modo reutilizar
un mismo cdigo innumerables veces.
Adems, la compilacin de un proyecto es ms rpida, ya que los objetos de la
biblioteca slo se compilan una vez. Si modicamos un mdulo concreto, solo ser
necesario recompilar ese mdulo y enlazar todo.
Por otro lado, en cada mdulo podemos tener un espacio de nombres local, de-
cidiendo que smbolos son globales y cuales no, cuales son accesibles por mdulos
ajenos y cuales son privados al mdulo actual.
La gran desventaja del modo relocalizable es la dicultad de controlar el banco
de memoria de datos empleado en cada momento. La naturaleza reubicable de este
modo, que otorga tantas ventajas de utilizacin, es un arma de doble lo que puede
obligar al desarrollador a incluir comprobaciones del banco utilizado cuando quiera
trabajar con un banco de memoria concreto.
Por supuesto, dentro de un cdigo relocalizable podemos incluir cdigo en mo-
do absoluto, jando una o varias rutinas, o variables, en posiciones de memoria
concretas y dejando el resto a eleccin del enlazador.
B.2. Herramientas
B.2.1. gpasm
gpasm es, como hemos presentado, el ensamblador de las GPUTILS que nos per-
mite pasar de un archivo en cdigo ensamblador a un objeto. Si el cdigo no tiene
smbolos sin instanciar, el objeto se puede usar directamente para programar el mi-
crocontrolador. La sintaxis es:
Compilador de C para el microcontrolador Microchip PIC18F4550 157
gpasm [opciones] programa.s
Las opciones ms importantes y comunes son:
-c: genera cdigo relocalizable.
-o archivo_salida: establece un nombre al objeto de salida. Por defecto ser el
mismo que el dado como entrada, pero cambiando la extensin por .o si el
cdigo es relocalizable o por .hex si es absoluto.
-p modelo_pic: especica para qu microcontrolador se va a compilar el archivo
de entrada. Tambin se puede hacer desde el cdigo ensamblador. En nuestro
caso ser -p 18F4550.
Existen ms opciones, pero para el trabajo cotidiano basta con conocer estas. De
todas formas es recomendable leer el manual para conocer todas sus posibilidades.
B.2.2. Cdigo fuente admitido por gpasm
La forma que toma una lnea de cdigo ensamblador vlido para gpasm es la
siguiente:
[etiqueta] instruccin [argumentos] [comentario]
Cada lnea del archivo contendr, como mximo, una instruccin en ensambla-
dor, seguida de los argumentos que sta requiera. El primer carcter del nemotc-
nico de la instruccin debe estar en una columna distinta a la primera de esa l-
nea. Opcionalmente, cada instruccin puede tener una etiqueta. Los comentarios
comienzan con el smbolo ;, a partir del cual nada de lo escrito ser tratado como
cdigo hasta el nal de la lnea. Las etiquetas sern una combinacin de letras, d-
gitos y el carcter _, con la nica restriccin de que el primer carcter no puede
ser un nmero. Las etiquetas, opcionalmente, irn seguidas por el carcter :, con
la nalidad de mejorar la lectura del programa.
Como ejemplo de instrucciones vlidas para gpasm mostramos el siguiente blo-
que de cdigo:
; Lnea en blanco
loop sleep ; Etiqueta y operacin
incf 6,1 ; Operacin con dos parmetros
goto loop ; Operacin con un parmetro
gpasm establece varias formas de especicar nmeros. Las ms utilizadas son
las binarias, decimales y hexadecimales. Todas se basan en una letra de prejo que
Compilador de C para el microcontrolador Microchip PIC18F4550 158
establece el tipo y el nmero en el formato adecuado. El prejo es B para binario,
D decimal y H para hexadecimal. Para los nmeros en hexadecimal existe otra re-
presentacin alternativa que consiste en el prejo 0x seguido del nmero. Tambin
se aceptan formatos alternativos heredados de MPASM. Lo vemos brevemente con
ejemplos:
Binario B1111011
1111011b
Decimal D123
123d
.123
Hexadecimal H7B
7Bh
0x7b
Por defecto, si no se especica un prejo o sujo para un nmero, gpasm lo inter-
pretar como hexadecimal, aunque es posible cambiar este comportamiento desde
la lnea de comandos.
gpasm admite expresiones basadas en el conjunto de operadores de C. Como
ejemplo, y sin entrar en profundidad, mostramos la siguiente secuencia vlida:
movlw (0x56 >> 4) & 0x0F
Esta instruccin carga en WREG el valor 56 hexadecimal (86 decimal), desplazn-
dolo 4 posiciones a la derecha y operando conjuntamente con 0x0F, quedando slo
los 4 bits menos signicativos, en este caso 1010 binario (10 decimal). Tenemos suma,
resta, desplazamientos, operaciones lgicas y comparaciones, entre otros operan-
dos, que nos proporcinan una gran exibilidad a la hora de utilizar expresiones
matemticas constantes, teniendo en cuenta que estas son evaluadas antes de tra-
ducir cdigo.
Analizando un poco ms el preprocesador, presentamos tres directivas ms. La
directiva include permite incluir archivos externos en el cdigo actual; es til, por
ejemplo, para separar las deniciones propias de cada procesador en distintos ar-
chivos e incluir en el cdigo slo la necesaria para el PIC con el que estamos traba-
jando. Otra directiva conocida en casi todos los precompiladores de diversos len-
guajes es dene, y su correspondiente undene, que permite asociar (o desasociar
respectivamente) smbolos signicativos con valores menos manejables y utilizar-
los directamente en el cdigo. Por ejemplo:
#define altura D25000
Compilador de C para el microcontrolador Microchip PIC18F4550 159
movlw altura
Un uso habitual de la directiva dene es dar nombres simblicos a las direcciones
de memoria de los distintos dispositivos que ofrece el microcontrolador. De este
modo, la legibilidad y portabilidad del cdigo es mayor que si emplesemos los
valores numricos.
Veremos a continuacin ms directivas de forma rpida, dado que el objetivo
de este apndice es tener slo una idea clara de las directivas que se usan para
la generacin de cdigo en el compilador de C. Para obtener ms informacin y
conocer el resto de las directivas recomendamos leer el manual de GPUTILS.
ORG posicin. En modo absoluto indica la posicin exacta donde se cargar el
cdigo escrito a continuacin de esta directiva. Si no se especica una posi-
cin, gpasm usar 0x000000.
$: esta etiqueta indica la direccin de la instruccin que la contiene. Es til para
escribir bucles en distancias relativas a la instruccin actual. Por ejemplo: goto
$ - 2.
BANKISEL etiqueta. Esta macro sirve para crear el cdigo que selecciona el
banco de memoria de datos que contiene la direccin de etiqueta para poder
efectuar accesos indirectos.
BANKSEL etiqueta. Al igual que la anterior, genera el cdigo de seleccin del
banco de etiqueta pero para accesos directos. Actuar de forma diferente segn
las caractersticas del PIC utilizado.
etiqueta CODE expresin. En modo relocalizable, indica que el bloque de cdigo
desde la directiva hasta el nal del archivo o hasta el siguiente bloque consti-
tuye una nueva seccin de cdigo mquina independiente del resto. Si no se
especica una etiqueta, se usar .code. La etiqueta expresin es opcional e indi-
ca la posicin exacta donde est localizada. Se usa a menudo para especicar
los vectores de inicio e interrupciones.
END. Marca el nal del cdigo fuente.
EXTERNsmbolo. En modo relocalizable, declara smbolo como denido en otro
archivo objeto y ser gplink quien se encargue de resolverlo. Se usa para im-
portar los parmetros y resultados de las bibliotecas.
GLOBAL smbolo. En modo relocalizable, declara smbolo como global, lo que
permite que sea utilizado en otros archivos objeto donde se necesite, usando
en estos la directiva EXTERN.
Compilador de C para el microcontrolador Microchip PIC18F4550 160
etiqueta RES n_bytes. Hace que gpasm reserve n_bytes de memoria en la posi-
cin en la que aparezca esta directiva. til para reservar espacio y referenciar
el mismo haciendo uso de etiqueta.
etiqueta UDATA expresin. Usado en modo relocalizable, indica que el bloque
de memoria desde la directiva hasta el siguiente bloque o el nal del archi-
vo constituyen una nueva seccin de memoria de datos sin inicializar. Es til,
junto con RES, para reservar espacio de memoria para las variables del cdi-
go. La etiqueta expresin se usa opcionalmente para jar la direccin donde
comienza el bloque.
Posteriormente veremos un ejemplo de cdigo completo. Para completar infor-
macin podemos revisar el manual de GPUTILS o consultar el manual de MPASM,
dado que todas las directivas del ensamblador propietario de Microchip estn dis-
ponibles en gpasm.
B.2.3. gplink
gplink es el enlazador de cdigo reubicable. Se encarga de relocalizar y enlazar
los archivos objeto que forman un programa, y crear un nico archivo listo para
transferir a la memoria de nuestro microcontrolador. Su sintaxis es:
gplink [opciones] [objetos] [bibliotecas]
Entre sus opciones, las ms importantes y tiles para el trabajo cotidiano son:
-I directorio. Especica un directorio para incluir en la bsqueda de archivos,
pudiendo existir varios parmetros -I en una misma llamada a gplink.
-o programa.hex. Establece un nombre alternativo al programa compilado. Por
defecto ser a.hex.
-s archivo. Especica el archivo con las deniciones necesarias para el proceso
de enlazado, correspondientes al microcontrolador especicado. Este archivo,
llamado linker script, indica la memoria disponible, su tipo y localizacin, y
dems parmetros necesarios para que el enlazador haga su trabajo. Si no
se especica ningn archivo, gplink usar el indicado en los cdigos objeto
pasados como argumentos.
Compilador de C para el microcontrolador Microchip PIC18F4550 161
B.2.4. gplib
gplib crea, modica y extrae archivos objeto de bibliotecas. Con esto podemos
agrupar una coleccin de objetos en un nico archivo y pasar este nico archivo a
gplink. gplink tomar solamente los objetos que necesite de la biblioteca, por lo que
el tamao nal del programa no excede ms all de lo estrictamente necesario. Su
sintaxis es:
gplib [opciones] biblioteca [objetos]
Entre las opciones, las ms importantes y tiles para el trabajo cotidiano son:
-c. Crea una nueva biblioteca.
-d. Borra objetos de una biblioteca.
-r. Aade o reemplaza un objeto de una biblioteca.
-s. Muestra los smbolos globales de una biblioteca.
-t. Muestra los objetos de una biblioteca.
-x. Extrae los objetos de una biblioteca.
Supongamos como ejemplo que tenemos los siguientes archivos:
mult.o
add.o
sub.o
div.o
La forma de crear una biblioteca con los tres primeros archivos objeto utilizando
gplib es sta:
gplib -c math.a mult.o add.o sub.o
Si posteriormente deseamos aadir div.o, o actualizarlo si ya estuviese en la bi-
blioteca, lo haramos de esta manera:
gplib -r math.a div.o
Compilador de C para el microcontrolador Microchip PIC18F4550 162
B.3. Compilando ensamblador
Veamos dos ejemplos de archivos ensamblador vlidos.
El archivo funcion1.s:
extern suma
extern op1, op2
global result
resultado UDATA
resul res 1
_funcion_llamante CODE
funcion1_ll:
BANKSEL op1
movlw 0x01
movwf op1
movlw 0x04
movwf op2
call suma
goto $
_reset CODE 0x0
goto funcion1_ll
nop
nop
retfie
END
Y el archivo add.s:
extern result
global op1, op2
global suma
operadores UDATA
op1 res 1
op2 res 1
Compilador de C para el microcontrolador Microchip PIC18F4550 163
Opsuma CODE
suma:
BANKSEL op1
movf op1, W
addwf op2, W
BANKSEL result
movwf result
return
END
Examinando los cheros vemos como en funcion1.s estn declarados suma, op1
y op2 como smbolos externos, indicando con ello que se pueden usar pero que no
estn declarados en este archivo. Al declarar result como global le damos visibilidad
desde otros archivos.
Observamos cmo en add.s se utilizan los mismos smbolos pero de manera con-
traria, dando visibilidad a op1, op2 y suma, y permitiendo el uso de result pese a no
estar declarado.
Con los segmentos UDATA designamos secciones, sin inicializar, de memoria
de datos: un byte en el primer archivo y dos bytes en el segundo. No especicamos
dnde se establecern sino el tamao que ocuparn y un nombre con el que inden-
ticarlos a ellos. Es habitual emplear una nica etiqueta para referirse a la direccin
base de una seccin y sumarle un offset cuando direccionemos bytes ms alla del
correspondiente a la base.
Con los segmentos CODE ocurre exactamente lo mismo: establecemos una sec-
cin de cdigo pero no damos ninguna indicacin de dnde situarlo. En este seg-
mento podemos ver como en el primer archivo, antes de poder usar el smbolo op1,
debemos usar BANKSEL para seleccionar el banco correcto en un acceso directo.
Dado que los operadores, op1 y op2, estn denidos dentro de la misma seccin,
estos estn en el mismo banco de memoria de datos, as que no es necesario usar
BANKSEL una vez direccionado uno de ellos.
Este primer archivo termina con GOTO $, el cual resulta en un bucle innito.
De esta manera el microcontrolador queda activo pero sin realizar ninguna opera-
cin aparte de esta espera activa sin n.
Existe otro segmento de cdigo, denido en el primer archivo, llamado _reset.
Con ste s indicamos una posicin donde debe ubicarse, la posicin 0x0. Esta di-
reccin corresponde al vector de inicializacin del microcontrolador y es donde se
encontrar la primera instruccin que ejecutar el PIC cuando se encienda.
Examinados nuestros cheros de ejemplo pasamos a ensamblarlos. Esto lo ha-
cemos con las siguientes llamadas a gpasm:
Compilador de C para el microcontrolador Microchip PIC18F4550 164
gpasm -c -p18F4550 funcion1.s
gpasm -c -p18F4550 add.s
Obtendremos dos archivos objeto, add.o y funcion1.o. Estos dos archivos no son
ejecutables hasta haber efectuado la fase de enlazado, ya que contiene smbolos
sin resolver. Adems de los cheros objeto, tendremos dos archivos (uno por cada
objeto) con el mismo nombre y de extensin .lst, que contendrn datos importantes
para la depuracin, como una lista de smbolos generados, el cdigo mquina de
las instrucciones o la posicin relativa de las instrucciones respecto al segmento.
Despus de la fase de ensamblado, llamamos al enlazador para que complete el
proceso de compilacin de nuestro programa en ensamblador:
gplink add.o funcion1.o -o programa.hex
El resultado del proceso de enlazado son tres archivos. El primero de estos archi-
vos contiene el programa ejecutable que escribiremos en la ash del PIC; su nombre,
para este ejemplo, es programa.hex. Los otros dos archivos son programa.cod, que nos
sirve para depurar el programa con algn software de simulacin, y programa.lst, que
contiene el listado de memoria de nuestro programa, con direcciones de memoria
absolutas.
Apndice C
Programas de ejemplo
El Hola, mundo en el campo de la programacin de microcontroladores es un
programa como el siguiente:
#include <p18f4550.h>
void main(void)
{
TRISD = 0;
PORTD = 1;
while(1)
{
PORTD = ~PORTD & 0x1;
delayms(500);
}
}
Su propsito es hacer que parpadee un diodo LED conectado al pin 1 del puerto
D con un perodo de un segundo. Un montaje electrnico sencillo para usar este
programa consiste, adems de las tomas de alimentacin y la seal de reloj, en un
LED conectado, junto a su resistencia de polarizacin, al pin 1 del puerto D. Este
programa comienza inicializando el puerto D como salida y, acto seguido, entra en
un bucle innito, donde cambia el valor de salida del pin 1 y ejecuta un retardo de
500ms mediante la funcin de biblioteca delayms, ante de la siguiente iteracin.
El archivo de cabecera p18f4550.h, incluido al comienzo del cdigo, dene una
serie de macros y constantes de preprocesador que facilitan la programacin de es-
te microcontrolador, dando nombres autodescriptivos para hacer referencia a los
registros especiales del microcontrolador. Por ejemplo, la constante TRISD es reem-
165
Compilador de C para el microcontrolador Microchip PIC18F4550 166
plazada por la cadena (* (unsigned char *) 0x088). De este modo, el programador
no necesita aprenderse los detalles de direccionamiento del microcontrolador y pue-
de escribir un cdigo ms legible y menos propenso a errores. Adems, el cdigo
escrito de esta manera es mucho ms sencillo de portar a otros modelos de micro-
controlador, limitndose a cambiar el archivo de cabecera en la mayora de casos.
En la fase de enlazado de cdigo debemos combinar nuestro programa, ya com-
pilado y ensamblado, con el cdigo objeto de la funcin delayms. Con esto obten-
dremos un programa ejecutable completo que har que nuestro microncontrolador
genere una onda cuadrada, la cual que se traduce en un parpadeo peridico del led
conectado al pin de salida.
Veamos un ejemplo basado en el programa anterior pero ms vistoso que el sen-
cillo LEDparpadeante: una luz que rebota en los extremos de un segmento (conocido
popularmente como efecto del coche fantstico). Para este ejemplo har falta conectar
un LED a cada uno de los 8 pines del puerto D.
#include <p18f4550.h>
void main(void)
{
TRISD=0;
while (1)
{
PORTD = 1;
while (PORTD)
{
delayms(50);
PORTD = PORTD << 1;
}
PORTD = 1 << 7;
while (PORTD)
{
delayms(50);
PORTD = PORTD >> 1;
}
}
}
Compilador de C para el microcontrolador Microchip PIC18F4550 167
Un ejemplo ms complicado que es posible implementar con el mismo montaje
hardware que el ejemplo anterior es mostrar una secuencia cclica con los primeros
valores de la sucesin de Fibonacci codicados en binario.
void main(void)
{
unsigned char f1, f2;
TRISD = 0;
while (1)
{
f1 = 1;
f2 = 1;
while (1)
{
PORTD = f1;
f1 += f2;
delayms(1000);
if (f1 == 233)
break;
PORTD = f2;
f2 += f1;
delayms(1000);
if (f2 == 233)
break;
}
}
}
Este cdigo hace uso de dos variables para almacenar los dos nmeros anterio-
res en la secuencia, que seran los necesarios para construir la sucesin de Fibonacci.
Aunque puede resultar un cdigo algo complejo para una implementacin de la
sucesin de nmeros de Fibonacci, sirve para ilustrar la manera de emplear sin pro-
blemas varias variables en conjuncin con los registros-la del microcontrolador.
Recordemos que los registros-la son punteros desreferenciados, por lo que se ma-
nejan como variables normales.
Compilador de C para el microcontrolador Microchip PIC18F4550 168
Para terminar, veamos un ejemplo que hace uso de una entrada desde el exterior
del microcontrolador. Este ejemplo usar el USART para comunicarse con un PC
convencional.
void main(void)
{
unsigned char i;
unsigned char tmp;
char datoadc[4];
datoadc[3] = \0;
/
*
El led parpadear tres veces, indicando
el comienzo del proceso.
*
/
TRISD = 0;
for (i = 1 ; i <= 3 ; i++)
{
PORTD = 0;
delayms(500);
PORTD = 1;
delayms(500);
}
/
*
Informar al PC por el puerto serie.
*
/
Inicializar_UART(9600);
Send_String("Hola\n\r");
while (1)
{
/
*
Espera a leer del puerto serie un
caracter p o P.
*
/
tmp = Get_Byte();
if (tmp == P || tmp == p)
{
/
*
Enva por el puerto serie diez
valores tomados con el conversor
A/D con un segundo de tiempo
entre muestras.
*
/
Compilador de C para el microcontrolador Microchip PIC18F4550 169
for (i = 0 ; i < 10 ; i++)
{
Send_Byte(i + 0);
Send_Byte(.);
int2char(leerAD(), datoadc);
Send_String(datoadc);
Send_Byte(\n);
Send_Byte(\r);
delayms(1000);
}
}
}
}
Al inicio del programa emitimos tres destellos en el primer LED del puerto D,
dejando ste encendido, indicando que el dispositivo est encendido. Acto seguido,
conguramos el USART a 9600 baudios 8N1 (8 bits, sin paridad y con un bit de pa-
rada) y enviamos la cadena Hola por el USART. Tras esto, esperamos la recepcin
de la letra P (mayscula o minscula), momento en el que enviamos diez muestras
del conversor A/D, ya convertidas a cadenas de caracteres.
Apndice D
Manual de usuario
Para programar los microcontroladores en lenguaje C con GCC necesitamos, l-
gicamente, el compilador GCC, pero adems varias herramientas software y hardware
que lo complementan. En este captulo es una breve presentacin de estas herra-
mientas y una introduccin a sus formas de uso.
D.1. GCC
El compilador GCC, a estas alturas, no necesita presentacin. Su cometido es
traducir un cdigo escrito en un lenguaje de alto nivel, C para el caso que nos afecta,
y generar un cdigo ensamblador equivalente. Adems, GCC puede encargarse de
manejar las otras utilidades software, por lo que el proceso de compilacin termina
reducindose a la mera invocacin de GCC con los argumentos adecuados.
La sintaxis bsica del port de GCC para PIC18 es:
pic18-gcc [opciones] programa.c
Las opciones de GCC utilizadas con ms frecuencia son las siguientes:
-E: slo ejecuta el preprocesador. Esta opcin es til para comprobar el funcio-
namiento de las macros del preprocesador que utilicemos en nuestro progra-
ma.
-S: slo ejecuta el preprocesador y compilador de C. Esta opcin resulta de
sumo inters cuando necesitamos vericar o ajustar a mano el cdigo ensam-
blador generado por GCC.
-c: Compila y ensambla pero no enlaza. Es habitual utilizar esta opcin cuando
compilamos programas compuestos por varios archivos de cdigo y automa-
tizamos el proceso de compilacin con el programa make.
170
Compilador de C para el microcontrolador Microchip PIC18F4550 171
-o nombre_de_archivo: especica el nombre del archivo de salida.
-On: nivel de optimizacin. GCC ofrece varios niveles generales de optimiza-
cin: 0,1,2,3 y s.
D.1.1. Ensamblador en lnea
GCC ofrece la posibilidad de introducir lneas de cdigo ensamblador, escrito a
mano, en el cdigo C de un programa. Estas lneas de ensamblador no pasan por
las etapas de compilacin del cdigo C, si no que pasan, salvando unos detalles que
ahora explicaremos, directamente al archivo de cdigo ensamblador resultante de
la compilacin. Las posibilidades que brinda esta funcionalidad son ilimitadas.
Para insertar un bloque de cdigo ensamblador entre nuestro cdigo C emplea-
remos la sentencia especial __asm__. La sintaxis de esta sentencia especial es la si-
guiente:
__asm__("bloque
de
instrucciones
en
ensamblador"
: lista de datos de salida
: lista de datos de entrada
: lista de datos sobreescritos);
El primer argumento es una cadena de caracteres con el cdigo ensamblador
que queremos obtener en el archivo nal. Este bloque de cdigo ensamblador no
tiene que ser independiente del resto del programa escrito en C, si no que puede
interactuar con las variables de ste que hayamos especicado en los otros tres ar-
gumentos. El segundo argumento es una lista de los registros y variables que escribe
nuestro bloque de cdigo ensamblador y cuyos valores pasarn a estar disponibles
para el resto del programa. El tercer argumento es una lista de los registros, varia-
bles y valores inmediatos cuyo valor en ese momento deseamos utilizar en el bloque
de cdigo ensamblador. Por ltimo, el cuarto argumento es una lista de los registros
y variables que sobreescribe nuestro cdigo ensamblador pero sin la necesidad de
que los valores de estos estn disponibles para el programa en C una vez que haya
terminado la ejecucin de este bloque en ensamblador.
Las instrucciones en ensamblador se reeren a los datos de salida, entrada y
temporales mediante las incgnitas %n. Estas incgnitas corresponden a cada uno
Compilador de C para el microcontrolador Microchip PIC18F4550 172
de los parmetros de las tres listas por orden de aparicin en las mismas. Asimis-
mo, es posible especicar unas restricciones de almacenamiento para las variables
y valores inmediatos utilizados en el bloque ensamblador que vamos a insertar. La
mejor manera de comprender esto es con el siguiente ejemplo:
char x = 1;
char y = 2;
__asm__ ("movf %1,W \n\t"
"movwf %0 \n"
: "=v" (x)
: "v" (y)
: );
Este bloque de cdigo ensamblador est compuesto por dos instrucciones, sepa-
radas por los caracteres de salto de lnea y tabulacin. Cada una de las instrucciones
hace uso de alguna de las variables de entrada o salida. La primera instruccin uti-
liza la incgnita %1, que se trata de una variable de entrada (tercer argumento de
la funcin __asm__) y con la restriccin de que debe estar almacenada en un regis-
tro virtual (restriccin v). Esta variable de entrada es la variable y del programa en
C. Por otro lado, la segunda instruccin del bloque en ensamblador opera con la
incgnita %0, que corresponde a la variable x del cdigo en C, con la restriccin de
que debe corresponder a un registro virtual y, adems, este registro ser sobreescrito
(carcter =) por el cdigo ensamblador dado.
Dado que GCC optimizar todo el cdigo que no resulte util, cuando se da el
caso de un bloque de cdigo ensamblador en lnea cuyos valores de salida no se
utilizan en el cdigo C posterior, el compilador elimina el bloque de cdigo ensam-
blador del programa nal. Para evitar esto podemos aplicar el atributo __volatile__
a la llamada a __asm__ para que GCC no haga esa optimizacin con el bloque de
ensamblador insertado a mano.
D.2. GNU Pic Utilities
El conjunto de utilidades GPUTILS proporciona un ensamblador (gpasm), un
enlazador (gplink) y un gestor de bibliotecas gplib, todos ellos para mltiples micro-
controladores PIC.
Compilador de C para el microcontrolador Microchip PIC18F4550 173
D.2.1. gpasm
gpasm es el ensamblador de las GPUTILS. Nos permite pasar de un archivo de
cdigo ensamblador a un objeto.
La sintaxis de gpasm es:
gpasm [opciones] programa.s
Las opciones ms importantes y comunes son:
-c: genera cdigo relocalizable.
-o archivo_salida: establece un nombre al objeto de salida. Por defecto ser el
mismo que el dado como entrada, pero cambiando la extensin por .o si el
cdigo es relocalizable o por .hex si es absoluto.
-p modelo_pic: especica para qu microcontrolador se va a compilar el archivo
de entrada. Tambin se puede hacer desde el cdigo ensamblador.
D.2.2. gplink
gplink es el enlazador de cdigo reubicable. Se encarga de relocalizar y enlazar
los archivos objeto que forman un programa, y crear un nico archivo listo para
transferir a la memoria de nuestro microcontrolador.
La sintaxis de gplink es:
gplink [opciones] [objetos] [bibliotecas]
Entre sus opciones, las ms importantes y tiles para el trabajo cotidiano son:
-I directorio. Especica un directorio para incluir en la bsqueda de archivos,
pudiendo existir varios parmetros -I en una misma llamada a gplink.
-o programa.hex. Establece un nombre alternativo al programa compilado. Por
defecto ser a.hex.
-s archivo. Especica el archivo con las deniciones necesarias para el proceso
de enlazado, correspondientes al microcontrolador especicado.
D.2.3. gplib
gplib crea, modica y extrae archivos objeto de bibliotecas. Con esto podemos
agrupar una coleccin de objetos en un nico archivo y pasar este nico archivo a
Compilador de C para el microcontrolador Microchip PIC18F4550 174
gplink. gplink tomar solamente los objetos que necesite de la biblioteca, por lo que
el tamao nal del programa no excede ms all de lo estrictamente necesario.
La sintaxis de gplib es:
gplib [opciones] biblioteca [objetos]
Las opciones ms importantes de gplib son:
-c. Crea una nueva biblioteca.
-d. Borra objetos de una biblioteca.
-r. Aade o reemplaza un objeto de una biblioteca.
-s. Muestra los smbolos globales de una biblioteca.
-t. Muestra los objetos de una biblioteca.
-x. Extrae los objetos de una biblioteca.
D.2.4. Programador
Para programar un PIC es indispensable disponer de un sistema programador
que soporte el modelo de microcontrolador deseado. Un programador consta de
dos partes: el dispositivo hardware y el programa software que lo maneja. Existen
numerosos dispositivos hardware que podemos fabricar o adquirirlos listos para uti-
lizar. Las caractersticas destacables de un programador son la interfaz que utiliza
para comunicacin con el ordenador y los modelos de microcontrolador que sopor-
ta.
Algunos ejemplos de dispositivos programadores son:
TE-20X (puerto serie)
GTP USB (puerto USB)
Como ejemplos de software para programar PIC tenemos los siguientes:
IC-Prog (Windows)
Odyssey (Linux)
WinPic800 (Windows)
Compilador de C para el microcontrolador Microchip PIC18F4550 175
D.2.5. Archivos auxiliares
Como complemento a nuestro compilador de C necesitamos unos archivos extra
que hacen posible la traduccin a ensamblador de un programa escrito en C.
Es indispensable disponer de la biblioteca libgcc.a, compilada junto con nuestro
back-end de GCC. Esta biblioteca incluye las funciones de inicializacin y reserva de
marcos de memoria que utilizarn nuestros programas durante su ejecucin.
Por otro lado, tambin debemos tener el archivo de deniciones para el enlaza-
dor de nuestro modelo de microcontrolador, el PIC18F4550. Este archivo se llama
p18f4550.lkr y, normalmente, viene incluido en el paquete GPUTILS del repositorio
de nuestra distribucin de Linux. La informacin que contiene este archivo es esen-
cial para que gplink pueda efectuar el proceso de enlazado para nuestro modelo de
microcontrolador.